mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation
@ 2026-08-19 14:37 Breno Leitao
  2026-08-19 14:37 ` [PATCH 1/5] workqueue: move the unbound-only attrs check to apply_workqueue_attrs() Breno Leitao
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

Per-cpu and unbound workqueues built their pwqs differently. This series
deletes alloc_and_link_percpu_pwqs() and sends per-cpu workqueues
through the apply_wqattrs_prepare() and apply_wqattrs_commit() path
unbound workqueues already use.

Now the branchj is alloc_pwq(), depending if we want a WQ_PERCPU or
UNBOUND workqueue.

The percpu affinity scope question is not answered yet: the backing
still comes from wq->flags, not the attrs, so nothing switches at
runtime.

This is one step closer, a pwq can now point at either pool.

Next is a WQ_AFFN_PERCPU scope that says the pools are per-cpu and
concurrency managed.

Then workqueue internals, such as alloc_pwq(), takes the backing from
the attrs, not from wq->flags, and WQ_PERCPU becomes a user-facing flag
only (?!)

PS: These changes make workqueue code -13 lines shorter.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (5):
      workqueue: move the unbound-only attrs check to apply_workqueue_attrs()
      workqueue: resolve the backing pool in alloc_pwq()
      workqueue: make the default pwq optional
      workqueue: build percpu pwqs through the attrs path
      workqueue: rename the pwq slot helpers shared with percpu

 kernel/workqueue.c | 157 ++++++++++++++++++++++++-----------------------------
 1 file changed, 72 insertions(+), 85 deletions(-)
---
base-commit: 4477a78374a57c3809b172ad30cceabda48c47c6
change-id: 20260817-wq_realsplit-86edfe9e7884

Best regards,
--  
Breno Leitao <leitao@debian.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/5] workqueue: move the unbound-only attrs check to apply_workqueue_attrs()
  2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
@ 2026-08-19 14:37 ` Breno Leitao
  2026-08-19 14:37 ` [PATCH 2/5] workqueue: resolve the backing pool in alloc_pwq() Breno Leitao
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

apply_workqueue_attrs_locked() refuses any workqueue without WQ_UNBOUND.
That is the right rule for changing a workqueue's attributes, but it also
blocks the internal callers, and building a percpu workqueue's pwq table
through this path needs them.

Move the check into apply_workqueue_attrs(), the exported entry point,
which is the one that changes attributes on a live workqueue.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/workqueue.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 59b0106624e24..632d3f75f0c96 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5568,10 +5568,6 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq,
 {
 	struct apply_wqattrs_ctx *ctx;
 
-	/* only unbound workqueues can change attributes */
-	if (WARN_ON(!(wq->flags & WQ_UNBOUND)))
-		return -EINVAL;
-
 	ctx = apply_wqattrs_prepare(wq, attrs, wq_unbound_cpumask);
 	if (IS_ERR(ctx))
 		return PTR_ERR(ctx);
@@ -5603,6 +5599,10 @@ int apply_workqueue_attrs(struct workqueue_struct *wq,
 {
 	int ret;
 
+	/* only unbound workqueues can change attributes */
+	if (WARN_ON(!(wq->flags & WQ_UNBOUND)))
+		return -EINVAL;
+
 	mutex_lock(&wq_pool_mutex);
 	ret = apply_workqueue_attrs_locked(wq, attrs);
 	mutex_unlock(&wq_pool_mutex);

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/5] workqueue: resolve the backing pool in alloc_pwq()
  2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
  2026-08-19 14:37 ` [PATCH 1/5] workqueue: move the unbound-only attrs check to apply_workqueue_attrs() Breno Leitao
@ 2026-08-19 14:37 ` Breno Leitao
  2026-08-19 14:37 ` [PATCH 3/5] workqueue: make the default pwq optional Breno Leitao
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

Unify the pool allocation in alloc_pwq(), instead of only getting the unbound
pool, and add a new parameter for the CPU.

Create a helper for unbound allocations (alloc_unbound_pwq()).

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/workqueue.c | 37 ++++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 632d3f75f0c96..7feefe0a4a5ff 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5376,22 +5376,34 @@ static struct worker_pool *get_percpu_pool(struct workqueue_struct *wq, int cpu)
 	return &per_cpu_ptr(pools, cpu)[highpri];
 }
 
-/* obtain a pool matching @attr and create a pwq associating the pool and @wq */
+/*
+ * Obtain the pool backing @wq on @cpu and create a pwq associating the two.
+ * A WQ_PERCPU workqueue is backed by the static per-cpu pool of @cpu,
+ * everything else by a pool matching @attrs. @cpu < 0 is always unbound.
+ */
 static struct pool_workqueue *alloc_pwq(struct workqueue_struct *wq,
-					const struct workqueue_attrs *attrs)
+					const struct workqueue_attrs *attrs,
+					int cpu)
 {
 	struct worker_pool *pool;
 	struct pool_workqueue *pwq;
 
 	lockdep_assert_held(&wq_pool_mutex);
 
-	pool = get_unbound_pool(attrs);
-	if (!pool)
-		return NULL;
+	WARN_ON_ONCE((wq->flags & WQ_PERCPU) && cpu < 0);
+
+	if (cpu >= 0 && (wq->flags & WQ_PERCPU)) {
+		pool = get_percpu_pool(wq, cpu);
+	} else {
+		pool = get_unbound_pool(attrs);
+		if (!pool)
+			return NULL;
+	}
 
 	pwq = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node);
 	if (!pwq) {
-		put_unbound_pool(pool);
+		if (!is_percpu_pool(pool))
+			put_unbound_pool(pool);
 		return NULL;
 	}
 
@@ -5399,6 +5411,13 @@ static struct pool_workqueue *alloc_pwq(struct workqueue_struct *wq,
 	return pwq;
 }
 
+/* create a pwq backed by an unbound pool matching @attrs */
+static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq,
+						const struct workqueue_attrs *attrs)
+{
+	return alloc_pwq(wq, attrs, -1);
+}
+
 /**
  * wq_calc_pod_cpumask - calculate a wq_attrs' cpumask for a pod
  * @attrs: the wq_attrs of the default pwq of the target workqueue
@@ -5500,7 +5519,7 @@ apply_wqattrs_prepare(struct workqueue_struct *wq,
 	copy_workqueue_attrs(new_attrs, attrs);
 	wqattrs_actualize_cpumask(new_attrs, unbound_cpumask);
 	cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask);
-	ctx->dfl_pwq = alloc_pwq(wq, new_attrs);
+	ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs);
 	if (!ctx->dfl_pwq)
 		goto out_free;
 
@@ -5510,7 +5529,7 @@ apply_wqattrs_prepare(struct workqueue_struct *wq,
 			ctx->pwq_tbl[cpu] = ctx->dfl_pwq;
 		} else {
 			wq_calc_pod_cpumask(new_attrs, cpu);
-			ctx->pwq_tbl[cpu] = alloc_pwq(wq, new_attrs);
+			ctx->pwq_tbl[cpu] = alloc_unbound_pwq(wq, new_attrs);
 			if (!ctx->pwq_tbl[cpu])
 				goto out_free;
 		}
@@ -5655,7 +5674,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 		return;
 
 	/* create a new pwq */
-	pwq = alloc_pwq(wq, target_attrs);
+	pwq = alloc_unbound_pwq(wq, target_attrs);
 	if (!pwq) {
 		pr_warn("workqueue: allocation failed while updating CPU pod affinity of \"%s\"\n",
 			wq->name);

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/5] workqueue: make the default pwq optional
  2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
  2026-08-19 14:37 ` [PATCH 1/5] workqueue: move the unbound-only attrs check to apply_workqueue_attrs() Breno Leitao
  2026-08-19 14:37 ` [PATCH 2/5] workqueue: resolve the backing pool in alloc_pwq() Breno Leitao
@ 2026-08-19 14:37 ` Breno Leitao
  2026-08-26  8:22   ` Marco Crivellari
  2026-08-19 14:37 ` [PATCH 4/5] workqueue: build percpu pwqs through the attrs path Breno Leitao
  2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao
  4 siblings, 1 reply; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

apply_wqattrs_prepare() always creates a dfl_pwq, the fallback a CPU
uses when it has no pwq of its own. This is a CPU unbound field, so, we
don't need it for the per cpu affinity version of workqueue.

A percpu workqueue has no use for one. Skip the allocation for
a WQ_PERCPU workqueue and skip installing what was not allocated.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/workqueue.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 7feefe0a4a5ff..8fac93574becb 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5513,15 +5513,18 @@ apply_wqattrs_prepare(struct workqueue_struct *wq,
 
 	/*
 	 * If something goes wrong during CPU up/down, we'll fall back to
-	 * the default pwq covering whole @attrs->cpumask.  Always create
-	 * it even if we don't use it immediately.
+	 * the default pwq covering whole @attrs->cpumask.  Create it even
+	 * if we don't use it immediately.  A percpu workqueue has a pwq on
+	 * every possible CPU and never falls back, so it has no default.
 	 */
 	copy_workqueue_attrs(new_attrs, attrs);
 	wqattrs_actualize_cpumask(new_attrs, unbound_cpumask);
 	cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask);
-	ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs);
-	if (!ctx->dfl_pwq)
-		goto out_free;
+	if (!(wq->flags & WQ_PERCPU)) {
+		ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs);
+		if (!ctx->dfl_pwq)
+			goto out_free;
+	}
 
 	for_each_possible_cpu(cpu) {
 		if (new_attrs->ordered) {
@@ -5573,7 +5576,8 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx)
 	for_each_possible_cpu(cpu)
 		ctx->pwq_tbl[cpu] = install_unbound_pwq(ctx->wq, cpu,
 							ctx->pwq_tbl[cpu]);
-	ctx->dfl_pwq = install_unbound_pwq(ctx->wq, -1, ctx->dfl_pwq);
+	if (ctx->dfl_pwq)
+		ctx->dfl_pwq = install_unbound_pwq(ctx->wq, -1, ctx->dfl_pwq);
 
 	/* update node_nr_active->max, which only unbound workqueues have */
 	if (ctx->wq->flags & WQ_UNBOUND)

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 4/5] workqueue: build percpu pwqs through the attrs path
  2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
                   ` (2 preceding siblings ...)
  2026-08-19 14:37 ` [PATCH 3/5] workqueue: make the default pwq optional Breno Leitao
@ 2026-08-19 14:37 ` Breno Leitao
  2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao
  4 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

Drop the WQ_PERCPU allocation path (alloc_and_link_percpu_pwqs()).

Now that alloc_pwq() hands out the static percpu pool for a CPU and the
default pwq is optional, the unbound path produces the same result.

Point a percpu workqueue at it and drop alloc_and_link_percpu_pwqs().

Nothing is installed in wq->cpu_pwq when the attrs path fails, since
apply_wqattrs_commit() does not run, so the enomem cleanup only has the
percpu array left to free.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/workqueue.c | 66 +++++++++++++-----------------------------------------
 1 file changed, 15 insertions(+), 51 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 8fac93574becb..2798a24e0c24a 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -516,6 +516,9 @@ static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS];
 /* I: attributes used when instantiating ordered pools on demand */
 static struct workqueue_attrs *ordered_wq_attrs[NR_STD_WORKER_POOLS];
 
+/* I: attributes of percpu workqueues, which are backed by the static pools */
+static struct workqueue_attrs *percpu_std_wq_attrs[NR_STD_WORKER_POOLS];
+
 /*
  * I: kthread_worker to release pwq's. pwq release needs to be bounced to a
  * process context while holding a pool lock. Bounce to a dedicated kthread
@@ -5532,7 +5535,7 @@ apply_wqattrs_prepare(struct workqueue_struct *wq,
 			ctx->pwq_tbl[cpu] = ctx->dfl_pwq;
 		} else {
 			wq_calc_pod_cpumask(new_attrs, cpu);
-			ctx->pwq_tbl[cpu] = alloc_unbound_pwq(wq, new_attrs);
+			ctx->pwq_tbl[cpu] = alloc_pwq(wq, new_attrs, cpu);
 			if (!ctx->pwq_tbl[cpu])
 				goto out_free;
 		}
@@ -5702,34 +5705,10 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 	put_pwq_unlocked(old_pwq);
 }
 
-static int alloc_and_link_percpu_pwqs(struct workqueue_struct *wq)
-{
-	struct pool_workqueue *pwq;
-	int cpu;
-
-	for_each_possible_cpu(cpu) {
-		struct worker_pool *pool = get_percpu_pool(wq, cpu);
-
-		pwq = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node);
-		if (!pwq)
-			return -ENOMEM;
-
-		init_pwq(pwq, wq, pool);
-
-		mutex_lock(&wq->mutex);
-		link_pwq(pwq);
-		mutex_unlock(&wq->mutex);
-
-		rcu_assign_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu), pwq);
-	}
-
-	return 0;
-}
-
 static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 {
 	bool highpri = wq->flags & WQ_HIGHPRI;
-	int cpu, ret;
+	int ret;
 
 	lockdep_assert_held(&wq_pool_mutex);
 
@@ -5738,7 +5717,7 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 		goto enomem;
 
 	if (!(wq->flags & WQ_UNBOUND)) {
-		ret = alloc_and_link_percpu_pwqs(wq);
+		ret = apply_workqueue_attrs_locked(wq, percpu_std_wq_attrs[highpri]);
 	} else if (wq->flags & __WQ_ORDERED) {
 		struct pool_workqueue *dfl_pwq;
 
@@ -5757,27 +5736,8 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 	return 0;
 
 enomem:
-	if (wq->cpu_pwq) {
-		for_each_possible_cpu(cpu) {
-			struct pool_workqueue __rcu **slot;
-			struct pool_workqueue *pwq;
-
-			slot = per_cpu_ptr(wq->cpu_pwq, cpu);
-			pwq = rcu_access_pointer(*slot);
-			if (pwq) {
-				/*
-				 * Unlink pwq from wq->pwqs since link_pwq()
-				 * may have already added it. wq->mutex is not
-				 * needed as the wq has not been published yet.
-				 */
-				if (!list_empty(&pwq->pwqs_node))
-					list_del_rcu(&pwq->pwqs_node);
-				kmem_cache_free(pwq_cache, pwq);
-			}
-		}
-		free_percpu(wq->cpu_pwq);
-		wq->cpu_pwq = NULL;
-	}
+	free_percpu(wq->cpu_pwq);
+	wq->cpu_pwq = NULL;
 	return -ENOMEM;
 }
 
@@ -6021,10 +5981,10 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt,
 	 * flushing the pwq_release_worker ensures that the pwq_release_workfn()
 	 * completes before calling kfree(wq).
 	 */
-	if (wq->flags & WQ_UNBOUND) {
+	if (pwq_release_worker)
 		kthread_flush_worker(pwq_release_worker);
+	if (wq->flags & WQ_UNBOUND)
 		free_node_nr_active(wq->node_nr_active);
-	}
 err_free_wq:
 	free_workqueue_attrs(wq->attrs);
 	kfree(wq);
@@ -8190,7 +8150,7 @@ void __init workqueue_init_early(void)
 			init_cpu_worker_pool(pool, cpu, std_nice[i++]);
 	}
 
-	/* create default unbound and ordered wq attrs */
+	/* create default unbound, ordered and percpu wq attrs */
 	for (i = 0; i < NR_STD_WORKER_POOLS; i++) {
 		struct workqueue_attrs *attrs;
 
@@ -8206,6 +8166,10 @@ void __init workqueue_init_early(void)
 		attrs->nice = std_nice[i];
 		attrs->ordered = true;
 		ordered_wq_attrs[i] = attrs;
+
+		BUG_ON(!(attrs = alloc_workqueue_attrs()));
+		attrs->nice = std_nice[i];
+		percpu_std_wq_attrs[i] = attrs;
 	}
 
 	system_wq = alloc_workqueue("events", WQ_PERCPU | __WQ_DEPRECATED, 0);

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu
  2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
                   ` (3 preceding siblings ...)
  2026-08-19 14:37 ` [PATCH 4/5] workqueue: build percpu pwqs through the attrs path Breno Leitao
@ 2026-08-19 14:37 ` Breno Leitao
  4 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-19 14:37 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, pmladek, marco.crivellari, gustavold, david.dai,
	Breno Leitao, kernel-team

install_unbound_pwq() fills the pwq slots of a percpu workqueue as well now
that alloc_and_link_pwqs() sends it through the attrs path, and
unbound_pwq_slot() and unbound_pwq() have always addressed and read those
slots for every workqueue, percpu included, from destroy_workqueue().

Their names say otherwise.  Rename them to install_pwq(), pwq_slot() and
installed_pwq().

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 kernel/workqueue.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 2798a24e0c24a..3127ea9d0a422 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -742,7 +742,7 @@ static int worker_pool_assign_id(struct worker_pool *pool)
 }
 
 static struct pool_workqueue __rcu **
-unbound_pwq_slot(struct workqueue_struct *wq, int cpu)
+pwq_slot(struct workqueue_struct *wq, int cpu)
 {
        if (cpu >= 0)
                return per_cpu_ptr(wq->cpu_pwq, cpu);
@@ -751,9 +751,9 @@ unbound_pwq_slot(struct workqueue_struct *wq, int cpu)
 }
 
 /* @cpu < 0 for dfl_pwq */
-static struct pool_workqueue *unbound_pwq(struct workqueue_struct *wq, int cpu)
+static struct pool_workqueue *installed_pwq(struct workqueue_struct *wq, int cpu)
 {
-	return rcu_dereference_check(*unbound_pwq_slot(wq, cpu),
+	return rcu_dereference_check(*pwq_slot(wq, cpu),
 				     lockdep_is_held(&wq_pool_mutex) ||
 				     lockdep_is_held(&wq->mutex));
 }
@@ -768,7 +768,7 @@ static struct pool_workqueue *unbound_pwq(struct workqueue_struct *wq, int cpu)
  */
 static struct cpumask *unbound_effective_cpumask(struct workqueue_struct *wq)
 {
-	return unbound_pwq(wq, -1)->pool->attrs->__pod_cpumask;
+	return installed_pwq(wq, -1)->pool->attrs->__pod_cpumask;
 }
 
 static unsigned int work_color_to_flags(int color)
@@ -5450,10 +5450,10 @@ static void wq_calc_pod_cpumask(struct workqueue_attrs *attrs, int cpu)
 }
 
 /* install @pwq into @wq and return the old pwq, @cpu < 0 for dfl_pwq */
-static struct pool_workqueue *install_unbound_pwq(struct workqueue_struct *wq,
-					int cpu, struct pool_workqueue *pwq)
+static struct pool_workqueue *install_pwq(struct workqueue_struct *wq,
+					  int cpu, struct pool_workqueue *pwq)
 {
-	struct pool_workqueue __rcu **slot = unbound_pwq_slot(wq, cpu);
+	struct pool_workqueue __rcu **slot = pwq_slot(wq, cpu);
 	struct pool_workqueue *old_pwq;
 
 	lockdep_assert_held(&wq_pool_mutex);
@@ -5577,10 +5577,10 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx)
 
 	/* save the previous pwqs and install the new ones */
 	for_each_possible_cpu(cpu)
-		ctx->pwq_tbl[cpu] = install_unbound_pwq(ctx->wq, cpu,
-							ctx->pwq_tbl[cpu]);
+		ctx->pwq_tbl[cpu] = install_pwq(ctx->wq, cpu,
+						ctx->pwq_tbl[cpu]);
 	if (ctx->dfl_pwq)
-		ctx->dfl_pwq = install_unbound_pwq(ctx->wq, -1, ctx->dfl_pwq);
+		ctx->dfl_pwq = install_pwq(ctx->wq, -1, ctx->dfl_pwq);
 
 	/* update node_nr_active->max, which only unbound workqueues have */
 	if (ctx->wq->flags & WQ_UNBOUND)
@@ -5677,7 +5677,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 
 	/* nothing to do if the target cpumask matches the current pwq */
 	wq_calc_pod_cpumask(target_attrs, cpu);
-	if (wqattrs_equal(target_attrs, unbound_pwq(wq, cpu)->pool->attrs))
+	if (wqattrs_equal(target_attrs, installed_pwq(wq, cpu)->pool->attrs))
 		return;
 
 	/* create a new pwq */
@@ -5690,16 +5690,16 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 
 	/* Install the new pwq. */
 	mutex_lock(&wq->mutex);
-	old_pwq = install_unbound_pwq(wq, cpu, pwq);
+	old_pwq = install_pwq(wq, cpu, pwq);
 	goto out_unlock;
 
 use_dfl_pwq:
 	mutex_lock(&wq->mutex);
-	pwq = unbound_pwq(wq, -1);
+	pwq = installed_pwq(wq, -1);
 	raw_spin_lock_irq(&pwq->pool->lock);
 	get_pwq(pwq);
 	raw_spin_unlock_irq(&pwq->pool->lock);
-	old_pwq = install_unbound_pwq(wq, cpu, pwq);
+	old_pwq = install_pwq(wq, cpu, pwq);
 out_unlock:
 	mutex_unlock(&wq->mutex);
 	put_pwq_unlocked(old_pwq);
@@ -6173,12 +6173,12 @@ void destroy_workqueue(struct workqueue_struct *wq)
 	rcu_read_lock();
 
 	for_each_possible_cpu(cpu) {
-		put_pwq_unlocked(unbound_pwq(wq, cpu));
-		RCU_INIT_POINTER(*unbound_pwq_slot(wq, cpu), NULL);
+		put_pwq_unlocked(installed_pwq(wq, cpu));
+		RCU_INIT_POINTER(*pwq_slot(wq, cpu), NULL);
 	}
 
-	put_pwq_unlocked(unbound_pwq(wq, -1));
-	RCU_INIT_POINTER(*unbound_pwq_slot(wq, -1), NULL);
+	put_pwq_unlocked(installed_pwq(wq, -1));
+	RCU_INIT_POINTER(*pwq_slot(wq, -1), NULL);
 
 	rcu_read_unlock();
 }

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/5] workqueue: make the default pwq optional
  2026-08-19 14:37 ` [PATCH 3/5] workqueue: make the default pwq optional Breno Leitao
@ 2026-08-26  8:22   ` Marco Crivellari
  2026-08-26 16:33     ` Breno Leitao
  0 siblings, 1 reply; 8+ messages in thread
From: Marco Crivellari @ 2026-08-26  8:22 UTC (permalink / raw)
  To: Breno Leitao
  Cc: Tejun Heo, Lai Jiangshan, linux-kernel, pmladek, gustavold,
	david.dai, kernel-team

Hi Breno,

On Wed, Aug 19, 2026 at 4:37 PM Breno Leitao <leitao@debian.org> wrote:
>
> apply_wqattrs_prepare() always creates a dfl_pwq, the fallback a CPU
> uses when it has no pwq of its own. This is a CPU unbound field, so, we
> don't need it for the per cpu affinity version of workqueue.
>
> A percpu workqueue has no use for one. Skip the allocation for
> a WQ_PERCPU workqueue and skip installing what was not allocated.

What do you think about factoring this code out into a new function?
So that it can later be called also when a CPU (with the "preferred
percpu" flag set) is isolated through cgroup. I guess the dfl_pwq
should be allocated / freed accordingly (?)

Hope this makes sense.

Thanks!

-- 

Marco Crivellari

SUSE Labs

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/5] workqueue: make the default pwq optional
  2026-08-26  8:22   ` Marco Crivellari
@ 2026-08-26 16:33     ` Breno Leitao
  0 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-08-26 16:33 UTC (permalink / raw)
  To: Marco Crivellari
  Cc: Tejun Heo, Lai Jiangshan, linux-kernel, pmladek, gustavold,
	david.dai, kernel-team

On Wed, Aug 26, 2026 at 10:22:52AM +0200, Marco Crivellari wrote:
> Hi Breno,
> 
> On Wed, Aug 19, 2026 at 4:37 PM Breno Leitao <leitao@debian.org> wrote:
> >
> > apply_wqattrs_prepare() always creates a dfl_pwq, the fallback a CPU
> > uses when it has no pwq of its own. This is a CPU unbound field, so, we
> > don't need it for the per cpu affinity version of workqueue.
> >
> > A percpu workqueue has no use for one. Skip the allocation for
> > a WQ_PERCPU workqueue and skip installing what was not allocated.
> 
> What do you think about factoring this code out into a new function?
> So that it can later be called also when a CPU (with the "preferred
> percpu" flag set) is isolated through cgroup. I guess the dfl_pwq
> should be allocated / freed accordingly (?)

Makes sense as a direction: once a percpu workqueue loses a CPU it needs
somewhere to send that CPU's work, and after this patch it has no
dfl_pwq to fall back to.

Thanks for the review,
--breno

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-08-26 16:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
2026-08-19 14:37 ` [PATCH 1/5] workqueue: move the unbound-only attrs check to apply_workqueue_attrs() Breno Leitao
2026-08-19 14:37 ` [PATCH 2/5] workqueue: resolve the backing pool in alloc_pwq() Breno Leitao
2026-08-19 14:37 ` [PATCH 3/5] workqueue: make the default pwq optional Breno Leitao
2026-08-26  8:22   ` Marco Crivellari
2026-08-26 16:33     ` Breno Leitao
2026-08-19 14:37 ` [PATCH 4/5] workqueue: build percpu pwqs through the attrs path Breno Leitao
2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®