git: 9349214a2815 - main - mlx5: Preallocate ktls tags asynchronously

From: Andrew Gallatin <gallatin_at_FreeBSD.org>
Date: Thu, 06 Nov 2025 00:00:43 UTC
The branch main has been updated by gallatin:

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

commit 9349214a28152a3cb2424459976f571a9c8fc5df
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2025-11-05 23:58:33 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2025-11-05 23:58:33 +0000

    mlx5: Preallocate ktls tags asynchronously
    
    Change tag preallocation to happen asynchronously when an interface is
    brought up, so as to reduce boot times when preallocating tags.
    
    - A new mlx5-tls-prealloc_wq is allocated when preallocation is
      desired, and started when an interface is opened
    
    - The bulk of the prealloc code remains the same, except the
      allocations are now M_NOWAIT. M_NOWAIT is needed because, since the
      preallocation is done asynchronously, and since tag allocation is
      not instant, we could race with a real TLS session trying to
      allocate a tag. Note that in this case, we take allocation failure
      as a sign that we were unable to obtain the entire zone due to there
      being other consumers.  This was suggested by @markj as a way to
      keep things simple, after discussing why uma_zone_get_cur() didn't
      immediately report a fully allocated zone.  If this turns out to be
      problematic, we could use uma_zone_set_maxaction() to stop
      pre-allocations (also suggested by Mark)
    
    Reviewed by: glebius, kib, markj
    Sponsored by: Netflix
    Differential Revision: https://reviews.freebsd.org/D53570
---
 sys/dev/mlx5/mlx5_en/en_hw_tls.h      |  3 ++
 sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls.c | 53 +++++++++++++++++++++++++++++------
 sys/dev/mlx5/mlx5_en/mlx5_en_main.c   |  3 ++
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/sys/dev/mlx5/mlx5_en/en_hw_tls.h b/sys/dev/mlx5/mlx5_en/en_hw_tls.h
index d637314e040e..cd57d2ac5f72 100644
--- a/sys/dev/mlx5/mlx5_en/en_hw_tls.h
+++ b/sys/dev/mlx5/mlx5_en/en_hw_tls.h
@@ -82,6 +82,8 @@ struct mlx5e_tls {
 	struct sysctl_ctx_list ctx;
 	struct mlx5e_tls_stats stats;
 	struct workqueue_struct *wq;
+	struct workqueue_struct *prealloc_wq;
+	struct work_struct prealloc_work;
 	uma_zone_t zone;
 	uint32_t max_resources;		/* max number of resources */
 	int zone_max;
@@ -92,6 +94,7 @@ struct mlx5e_tls {
 int mlx5e_tls_init(struct mlx5e_priv *);
 void mlx5e_tls_cleanup(struct mlx5e_priv *);
 int mlx5e_sq_tls_xmit(struct mlx5e_sq *, struct mlx5e_xmit_args *, struct mbuf **);
+void mlx5e_tls_prealloc_tags(struct mlx5e_priv *priv);
 
 if_snd_tag_alloc_t mlx5e_tls_snd_tag_alloc;
 
diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls.c b/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls.c
index 6c83de5f3580..851316ccfcd7 100644
--- a/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls.c
+++ b/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls.c
@@ -80,23 +80,39 @@ static const char *mlx5e_tls_stats_desc[] = {
 };
 
 static void mlx5e_tls_work(struct work_struct *);
+static void mlx5e_tls_prealloc_work(struct work_struct *);
 
 /*
- * Expand the tls tag UMA zone in a sleepable context
+ * Expand the tls tag UMA zone in an async context
  */
 
 static void
-mlx5e_prealloc_tags(struct mlx5e_priv *priv, int nitems)
+mlx5e_tls_prealloc_work(struct work_struct *work)
 {
+	struct mlx5e_priv *priv;
+	struct mlx5e_tls *ptls;
 	struct mlx5e_tls_tag **tags;
-	int i;
+	int i, nitems;
+
+	ptls = container_of(work, struct mlx5e_tls, prealloc_work);
+	priv = container_of(ptls, struct mlx5e_priv, tls);
+	nitems = ptls->zone_max;
 
 	tags = malloc(sizeof(tags[0]) * nitems,
-	    M_MLX5E_TLS, M_WAITOK);
-	for (i = 0; i < nitems; i++)
-		tags[i] = uma_zalloc(priv->tls.zone, M_WAITOK);
+	    M_MLX5E_TLS, M_WAITOK | M_ZERO);
+	for (i = 0; i < nitems; i++) {
+		tags[i] = uma_zalloc(priv->tls.zone, M_NOWAIT);
+		/*
+		 * If the allocation fails, its likely we are competing
+		 * with real consumers of tags and the zone is full,
+		 * so exit the loop, and release the tags like we would
+		 * if we allocated all "nitems"
+		 */
+		if (tags[i] == NULL)
+			break;
+	}
 	__compiler_membar();
-	for (i = 0; i < nitems; i++)
+	for (i = 0; i < nitems && tags[i] != NULL; i++)
 		uma_zfree(priv->tls.zone, tags[i]);
 	free(tags, M_MLX5E_TLS);
 }
@@ -244,8 +260,6 @@ mlx5e_tls_init(struct mlx5e_priv *priv)
 	}
 
 	uma_zone_set_max(ptls->zone, ptls->zone_max);
-	if (prealloc_tags != 0)
-		mlx5e_prealloc_tags(priv, ptls->zone_max);
 
 	for (x = 0; x != MLX5E_TLS_STATS_NUM; x++)
 		ptls->stats.arg[x] = counter_u64_alloc(M_WAITOK);
@@ -270,6 +284,23 @@ mlx5e_tls_init(struct mlx5e_priv *priv)
 	return (0);
 }
 
+void
+mlx5e_tls_prealloc_tags(struct mlx5e_priv *priv)
+{
+	struct mlx5e_tls *ptls = &priv->tls;
+	int prealloc_tags = 0;
+
+	if (ptls->prealloc_wq != NULL)
+		return;
+
+	TUNABLE_INT_FETCH("hw.mlx5.tls_prealloc_tags", &prealloc_tags);
+	if (prealloc_tags == 0)
+		return;
+	ptls->prealloc_wq = create_singlethread_workqueue("mlx5-tls-prealloc_wq");
+	INIT_WORK(&ptls->prealloc_work, mlx5e_tls_prealloc_work);
+	queue_work(ptls->prealloc_wq, &ptls->prealloc_work);
+}
+
 void
 mlx5e_tls_cleanup(struct mlx5e_priv *priv)
 {
@@ -280,6 +311,10 @@ mlx5e_tls_cleanup(struct mlx5e_priv *priv)
 		return;
 
 	ptls->init = 0;
+	if (ptls->prealloc_wq != NULL) {
+		flush_workqueue(ptls->prealloc_wq);
+		destroy_workqueue(ptls->prealloc_wq);
+	}
 	flush_workqueue(ptls->wq);
 	sysctl_ctx_free(&ptls->ctx);
 	uma_zdestroy(ptls->zone);
diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
index f83506bda1aa..ee9c53bb0a60 100644
--- a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
+++ b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
@@ -3335,6 +3335,9 @@ mlx5e_open_locked(if_t ifp)
 
 	mlx5e_update_carrier(priv);
 
+	if ((if_getcapenable(ifp) & (IFCAP_TXTLS4 | IFCAP_TXTLS6)) != 0)
+		mlx5e_tls_prealloc_tags(priv);
+
 	return (0);
 
 err_close_channels: