git: e80e371d79d8 - main - if_wg: avoid sleeping under the net epoch

Kyle Evans kevans at FreeBSD.org
Tue Mar 9 11:17:54 UTC 2021


The branch main has been updated by kevans:

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

commit e80e371d79d825712a9078e0d1a14b62567291c4
Author:     Kyle Evans <kevans at FreeBSD.org>
AuthorDate: 2021-03-08 06:06:28 +0000
Commit:     Kyle Evans <kevans at FreeBSD.org>
CommitDate: 2021-03-09 11:16:30 +0000

    if_wg: avoid sleeping under the net epoch
    
    No sleeping allowed here, so avoid it.  Collect the subset of data we
    want inside of the epoch, as we'll need extra allocations when we add
    items to the nvlist.
    
    Reviewed by:    grehan (earlier version), markj
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D29124
---
 sys/dev/if_wg/module/module.c | 136 ++++++++++++++++++++++++++++++------------
 1 file changed, 99 insertions(+), 37 deletions(-)

diff --git a/sys/dev/if_wg/module/module.c b/sys/dev/if_wg/module/module.c
index a40a304616c7..6ae3bf9db022 100644
--- a/sys/dev/if_wg/module/module.c
+++ b/sys/dev/if_wg/module/module.c
@@ -69,6 +69,14 @@ MALLOC_DEFINE(M_WG, "WG", "wireguard");
 
 TASKQGROUP_DECLARE(if_io_tqg);
 
+struct wg_peer_export {
+	struct sockaddr_storage		endpoint;
+	uint8_t				public_key[WG_KEY_SIZE];
+	size_t				endpoint_sz;
+	struct wg_allowedip		*aip;
+	int				aip_count;
+};
+
 static int clone_count;
 uma_zone_t ratelimit_zone;
 
@@ -386,37 +394,70 @@ wg_stop(if_ctx_t ctx)
 	wg_socket_reinit(sc, NULL, NULL);
 }
 
-static nvlist_t *
-wg_peer_to_nvl(struct wg_peer *peer)
+static int
+wg_peer_to_export(struct wg_peer *peer, struct wg_peer_export *exp)
 {
-	struct wg_route *rt;
-	int i, count;
-	nvlist_t *nvl;
-	caddr_t key;
-	size_t sa_sz;
-	struct wg_allowedip *aip;
 	struct wg_endpoint *ep;
+	struct wg_route *rt;
+	int i;
 
-	if ((nvl = nvlist_create(0)) == NULL)
-		return (NULL);
-	key = peer->p_remote.r_public;
-	nvlist_add_binary(nvl, "public-key", key, WG_KEY_SIZE);
+	/* Non-sleepable context. */
+	NET_EPOCH_ASSERT();
+
+	bzero(&exp->endpoint, sizeof(exp->endpoint));
 	ep = &peer->p_endpoint;
 	if (ep->e_remote.r_sa.sa_family != 0) {
-		sa_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ?
-			sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
-		nvlist_add_binary(nvl, "endpoint", &ep->e_remote, sa_sz);
+		exp->endpoint_sz = (ep->e_remote.r_sa.sa_family == AF_INET) ?
+		    sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+
+		memcpy(&exp->endpoint, &ep->e_remote, exp->endpoint_sz);
 	}
-	i = count = 0;
+
+	memcpy(exp->public_key, peer->p_remote.r_public,
+	    sizeof(exp->public_key));
+
+	exp->aip_count = 0;
 	CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
-		count++;
+		exp->aip_count++;
 	}
-	aip = malloc(count*sizeof(*aip), M_TEMP, M_WAITOK);
+
+	/* Early success; no allowed-ips to copy out. */
+	if (exp->aip_count == 0)
+		return (0);
+
+	exp->aip = malloc(exp->aip_count * sizeof(*exp->aip), M_TEMP, M_NOWAIT);
+	if (exp->aip == NULL)
+		return (ENOMEM);
+
+	i = 0;
 	CK_LIST_FOREACH(rt, &peer->p_routes, r_entry) {
-		memcpy(&aip[i++], &rt->r_cidr, sizeof(*aip));
+		memcpy(&exp->aip[i++], &rt->r_cidr, sizeof(*exp->aip));
+		if (i == exp->aip_count)
+			break;
 	}
-	nvlist_add_binary(nvl, "allowed-ips", aip, count*sizeof(*aip));
-	free(aip, M_TEMP);
+
+	/* Again, AllowedIPs might have shrank; update it. */
+	exp->aip_count = i;
+
+	return (0);
+}
+
+static nvlist_t *
+wg_peer_export_to_nvl(struct wg_peer_export *exp)
+{
+	nvlist_t *nvl;
+
+	if ((nvl = nvlist_create(0)) == NULL)
+		return (NULL);
+
+	nvlist_add_binary(nvl, "public-key", exp->public_key, WG_KEY_SIZE);
+	if (exp->endpoint_sz != 0)
+		nvlist_add_binary(nvl, "endpoint", &exp->endpoint,
+		    exp->endpoint_sz);
+
+	nvlist_add_binary(nvl, "allowed-ips", exp->aip,
+	    exp->aip_count * sizeof(*exp->aip));
+
 	return (nvl);
 }
 
@@ -427,10 +468,8 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
 	int err, i, peer_count;
 	nvlist_t *nvl, **nvl_array;
 	struct epoch_tracker et;
-#ifdef INVARIANTS
-	void *packed;
-	size_t size;
-#endif
+	struct wg_peer_export *wpe;
+
 	nvl = NULL;
 	nvl_array = NULL;
 	if (nvl_arrayp)
@@ -447,34 +486,44 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
 
 	if (nvlp && (nvl = nvlist_create(0)) == NULL)
 		return (ENOMEM);
+
 	err = i = 0;
 	nvl_array = malloc(peer_count*sizeof(void*), M_TEMP, M_WAITOK);
+	wpe = malloc(peer_count*sizeof(*wpe), M_TEMP, M_WAITOK | M_ZERO);
+
 	NET_EPOCH_ENTER(et);
 	CK_LIST_FOREACH(peer, &sc->sc_hashtable.h_peers_list, p_entry) {
-		nvl_array[i] = wg_peer_to_nvl(peer);
-		if (nvl_array[i] == NULL) {
-			printf("wg_peer_to_nvl failed on %d peer\n", i);
+		if ((err = wg_peer_to_export(peer, &wpe[i])) != 0) {
+			printf("wg_peer_to_export failed on %d peer, error %d\n",
+			    i, err);
 			break;
 		}
-#ifdef INVARIANTS
-		packed = nvlist_pack(nvl_array[i], &size);
-		if (packed == NULL) {
-			printf("nvlist_pack(%p, %p) => %d",
-				   nvl_array[i], &size, nvlist_error(nvl));
-		}
-		free(packed, M_NVLIST);
-#endif	
+
 		i++;
 		if (i == peer_count)
 			break;
 	}
 	NET_EPOCH_EXIT(et);
+
+	if (err != 0)
+		goto out;
+
+	/* Update the peer count, in case we found fewer entries. */
 	*peer_countp = peer_count = i;
 	if (peer_count == 0) {
 		printf("no peers found in list\n");
 		err = ENOENT;
 		goto out;
 	}
+
+	for (i = 0; i < peer_count; i++) {
+		nvl_array[i] = wg_peer_export_to_nvl(&wpe[i]);
+		if (nvl_array[i] == NULL) {
+			printf("wg_peer_export_to_nvl failed on %d peer\n", i);
+			break;
+		}
+	}
+
 	if (nvl) {
 		nvlist_add_nvlist_array(nvl, "peer-list",
 		    (const nvlist_t * const *)nvl_array, peer_count);
@@ -486,8 +535,21 @@ wg_marshal_peers(struct wg_softc *sc, nvlist_t **nvlp, nvlist_t ***nvl_arrayp, i
 		*nvlp = nvl;
 	}
 	*nvl_arrayp = nvl_array;
-	return (0);
+	err = 0;
  out:
+	if (err != 0) {
+		for (i = 0; i < peer_count; i++) {
+			nvlist_destroy(nvl_array[i]);
+		}
+
+		free(nvl_array, M_TEMP);
+		if (nvl != NULL)
+			nvlist_destroy(nvl);
+	}
+
+	for (i = 0; i < peer_count; i++)
+		free(wpe[i].aip, M_TEMP);
+	free(wpe, M_TEMP);
 	return (err);
 }
 


More information about the dev-commits-src-all mailing list