git: df2b419a4105 - main - ifnet: add if_foreach_sleep() to allow ifnet iterations with sleep.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Mon, 06 Mar 2023 15:08:29 UTC
The branch main has been updated by melifaro:

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

commit df2b419a4105588384a89a57442ed6c6ca002455
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-03-04 10:09:09 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-03-06 15:08:08 +0000

    ifnet: add if_foreach_sleep() to allow ifnet iterations with sleep.
    
    Subscribers: imp, ae, glebius
    
    Differential Revision: https://reviews.freebsd.org/D38904
---
 sys/net/if.c     | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/net/if_var.h |  2 ++
 2 files changed, 67 insertions(+)

diff --git a/sys/net/if.c b/sys/net/if.c
index 58711061eb5e..f3ef822178ff 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -4535,6 +4535,71 @@ if_foreach(if_foreach_cb_t cb, void *cb_arg)
 	return (error);
 }
 
+/*
+ * Iterates over the list of interfaces, permitting callback function @cb to sleep.
+ * Stops iteration if @cb returns non-zero error code.
+ * Returns the last error code from @cb.
+ * @match_cb: optional match callback limiting the iteration to only matched interfaces
+ * @match_arg: argument to pass to @match_cb
+ * @cb: iteration callback
+ * @cb_arg: argument to pass to @cb
+ */
+int
+if_foreach_sleep(if_foreach_match_t match_cb, void *match_arg, if_foreach_cb_t cb,
+    void *cb_arg)
+{
+	int match_count = 0, array_size = 16; /* 128 bytes for malloc */
+	struct ifnet **match_array = NULL;
+	int error = 0;
+
+	MPASS(cb);
+
+	while (true) {
+		struct ifnet **new_array;
+		int new_size = array_size;
+		struct epoch_tracker et;
+		struct ifnet *ifp;
+
+		while (new_size < match_count)
+			new_size *= 2;
+		new_array = malloc(new_size * sizeof(void *), M_TEMP, M_WAITOK);
+		if (match_array != NULL)
+			memcpy(new_array, match_array, array_size * sizeof(void *));
+		free(match_array, M_TEMP);
+		match_array = new_array;
+		array_size = new_size;
+
+		match_count = 0;
+		NET_EPOCH_ENTER(et);
+		CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+			if (match_cb != NULL && !match_cb(ifp, match_arg))
+				continue;
+			if (match_count < array_size) {
+				if (if_try_ref(ifp))
+					match_array[match_count++] = ifp;
+			} else
+				match_count++;
+		}
+		NET_EPOCH_EXIT(et);
+
+		if (match_count > array_size) {
+			for (int i = 0; i < array_size; i++)
+				if_rele(match_array[i]);
+			continue;
+		} else {
+			for (int i = 0; i < match_count; i++) {
+				if (error == 0)
+					error = cb(match_array[i], cb_arg);
+				if_rele(match_array[i]);
+			}
+			free(match_array, M_TEMP);
+			break;
+		}
+	}
+
+	return (error);
+}
+
 u_int
 if_foreach_lladdr(if_t ifp, iflladdr_cb_t cb, void *cb_arg)
 {
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index c9b2de736d10..3e4d6c883c13 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -680,7 +680,9 @@ typedef u_int if_addr_cb_t(void *, struct ifaddr *, u_int);
 u_int if_foreach_addr_type(if_t ifp, int type, if_addr_cb_t cb, void *cb_arg);
 
 typedef int (*if_foreach_cb_t)(if_t, void *);
+typedef bool (*if_foreach_match_t)(if_t, void *);
 int	if_foreach(if_foreach_cb_t, void *);
+int	if_foreach_sleep(if_foreach_match_t, void *, if_foreach_cb_t, void *);
 
 /* Functions */
 void if_setinitfn(if_t ifp, if_init_fn_t);