* [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
` (5 more replies)
0 siblings, 6 replies; 10+ 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] 10+ 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
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ 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] 10+ 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
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ 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] 10+ 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
` (2 subsequent siblings)
5 siblings, 1 reply; 10+ 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] 10+ 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-31 22:23 ` Tejun Heo
2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao
2026-08-31 22:11 ` [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Tejun Heo
5 siblings, 1 reply; 10+ 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] 10+ 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
2026-08-31 22:11 ` [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Tejun Heo
5 siblings, 0 replies; 10+ 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] 10+ 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; 10+ 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] 10+ 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; 10+ 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] 10+ messages in thread
* Re: [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation
2026-08-19 14:37 [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Breno Leitao
` (4 preceding siblings ...)
2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao
@ 2026-08-31 22:11 ` Tejun Heo
5 siblings, 0 replies; 10+ messages in thread
From: Tejun Heo @ 2026-08-31 22:11 UTC (permalink / raw)
To: Breno Leitao
Cc: Lai Jiangshan, linux-kernel, pmladek, marco.crivellari,
gustavold, david.dai, kernel-team
Hello,
On Wed, Aug 19, 2026 at 07:37:34AM -0700, Breno Leitao wrote:
> 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
Applied 1-3 to wq/for-7.4.
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 4/5] workqueue: build percpu pwqs through the attrs path
2026-08-19 14:37 ` [PATCH 4/5] workqueue: build percpu pwqs through the attrs path Breno Leitao
@ 2026-08-31 22:23 ` Tejun Heo
0 siblings, 0 replies; 10+ messages in thread
From: Tejun Heo @ 2026-08-31 22:23 UTC (permalink / raw)
To: Breno Leitao
Cc: Lai Jiangshan, linux-kernel, pmladek, marco.crivellari,
gustavold, david.dai, kernel-team
Hello,
On Wed, Aug 19, 2026 at 07:37:38AM -0700, Breno Leitao wrote:
> if (!(wq->flags & WQ_UNBOUND)) {
> - ret = alloc_and_link_percpu_pwqs(wq);
> + ret = apply_workqueue_attrs_locked(wq, percpu_std_wq_attrs[highpri]);
Applied 4-5 to wq/for-7.4.
One note: if pwq allocation fails partway through
apply_wqattrs_prepare(), the already allocated pwqs are released through
pwq_release_worker, which isn't created until workqueue_init(). Percpu
workqueues are created before that, so an allocation failure in that
window now oopses instead of failing cleanly. An allocation failing that
early is unrecoverable anyway, so this doesn't need handling, but it
probably deserves a comment.
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-31 22:23 UTC | newest]
Thread overview: 10+ 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-31 22:23 ` Tejun Heo
2026-08-19 14:37 ` [PATCH 5/5] workqueue: rename the pwq slot helpers shared with percpu Breno Leitao
2026-08-31 22:11 ` [PATCH 0/5] workqueue: unify percpu and unbound pwq allocation Tejun Heo
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®