The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool
@ 2026-07-31 11:57 Breno Leitao
  2026-07-31 11:57 ` [PATCH 1/6] workqueue: factor out get_percpu_pool() Breno Leitao
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

The goal is to unify per-cpu and unbound workqueues onto one backend, so
an unbound workqueue can run on the concurrency-managed per-cpu pools and
WQ_PERCPU just requests that backend [1]. This series is preparation and
changes no behaviour on its own.

These changes also is a reasonable readability cleanup.

What will come later:

  * Introduce a concurrency-managed per-cpu affinity scope
    (WQ_AFFN_PERCPU_CM) — percpu as an affinity setting

  * Direct pool selection (fold the create-then-convert), and rename the
    helpers that are no longer unbound-specific: install_unbound_pwq(),
    unbound_pwq(), unbound_pwq_slot() (and alloc_unbound_pwq() -> alloc_pwq()).

  * Dynamic switching between per-cpu and unbound backing for CPU isolation.

Am I on the right path?

[1] https://lore.kernel.org/all/ak569WYSm3ygKl1-@slm.duckdns.org

Signed-off-by: Breno Leitao <leitao@debian.org>
---
Breno Leitao (6):
      workqueue: factor out get_percpu_pool()
      workqueue: factor out alloc_and_link_percpu_pwqs()
      workqueue: release pwq pools by pool type
      workqueue: account nr_active by the backing pool
      workqueue: add a per-cpu backend for unbound pwqs
      workqueue: install per-cpu pwqs at creation for __WQ_PERCPU_POOLS

 include/linux/workqueue.h |   1 +
 kernel/workqueue.c        | 143 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 105 insertions(+), 39 deletions(-)
---
base-commit: dbff1ec23f68640a743512e8ada125795d1b72c7
change-id: 20260729-wq-pool-refactor-0fb28721f83f

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


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

* [PATCH 1/6] workqueue: factor out get_percpu_pool()
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  2026-07-31 11:57 ` [PATCH 2/6] workqueue: factor out alloc_and_link_percpu_pwqs() Breno Leitao
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

Move the static per-cpu worker_pool lookup in alloc_and_link_pwqs() into
a helper, get_percpu_pool(), so the lookup can be shared by other
pool-selection paths.

No functional change.

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

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 4ec3db31493d2..63a39bb3f5e47 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5350,6 +5350,20 @@ static void link_pwq(struct pool_workqueue *pwq)
 	list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs);
 }
 
+/* Return the static per-cpu worker_pool that backs @wq on @cpu. */
+static struct worker_pool *get_percpu_pool(struct workqueue_struct *wq, int cpu)
+{
+	struct worker_pool __percpu *pools;
+	bool highpri = wq->flags & WQ_HIGHPRI;
+
+	if (wq->flags & WQ_BH)
+		pools = bh_worker_pools;
+	else
+		pools = cpu_worker_pools;
+
+	return &per_cpu_ptr(pools, cpu)[highpri];
+}
+
 /* obtain a pool matching @attr and create a pwq associating the pool and @wq */
 static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq,
 					const struct workqueue_attrs *attrs)
@@ -5664,19 +5678,9 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 		goto enomem;
 
 	if (!(wq->flags & WQ_UNBOUND)) {
-		struct worker_pool __percpu *pools;
-
-		if (wq->flags & WQ_BH)
-			pools = bh_worker_pools;
-		else
-			pools = cpu_worker_pools;
-
 		for_each_possible_cpu(cpu) {
-			struct pool_workqueue **pwq_p;
-			struct worker_pool *pool;
-
-			pool = &(per_cpu_ptr(pools, cpu)[highpri]);
-			pwq_p = per_cpu_ptr(wq->cpu_pwq, cpu);
+			struct pool_workqueue **pwq_p = per_cpu_ptr(wq->cpu_pwq, cpu);
+			struct worker_pool *pool = get_percpu_pool(wq, cpu);
 
 			*pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL,
 						       pool->node);

-- 
2.53.0-Meta


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

* [PATCH 2/6] workqueue: factor out alloc_and_link_percpu_pwqs()
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
  2026-07-31 11:57 ` [PATCH 1/6] workqueue: factor out get_percpu_pool() Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  2026-07-31 11:57 ` [PATCH 3/6] workqueue: release pwq pools by pool type Breno Leitao
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

Move the per-cpu pwq allocation loop out of alloc_and_link_pwqs() into a
helper. The inner allocation-failure path now returns -ENOMEM and the
caller jumps to the existing enomem cleanup, equivalent to the previous
goto.

Now that the per-cpu branch returns a value through the helper, the
per-cpu, ordered and unbound cases share a single error-handling tail.
Turn the separate per-cpu block and the ordered/unbound if/else into one
if/else-if/else chain, dropping the early return and the duplicated goto.

No functional change.

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

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 63a39bb3f5e47..b386a457c0381 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5666,6 +5666,28 @@ 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)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		struct pool_workqueue **pwq_p = per_cpu_ptr(wq->cpu_pwq, cpu);
+		struct worker_pool *pool = get_percpu_pool(wq, cpu);
+
+		*pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node);
+		if (!*pwq_p)
+			return -ENOMEM;
+
+		init_pwq(*pwq_p, wq, pool);
+
+		mutex_lock(&wq->mutex);
+		link_pwq(*pwq_p);
+		mutex_unlock(&wq->mutex);
+	}
+
+	return 0;
+}
+
 static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 {
 	bool highpri = wq->flags & WQ_HIGHPRI;
@@ -5678,25 +5700,8 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 		goto enomem;
 
 	if (!(wq->flags & WQ_UNBOUND)) {
-		for_each_possible_cpu(cpu) {
-			struct pool_workqueue **pwq_p = per_cpu_ptr(wq->cpu_pwq, cpu);
-			struct worker_pool *pool = get_percpu_pool(wq, cpu);
-
-			*pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL,
-						       pool->node);
-			if (!*pwq_p)
-				goto enomem;
-
-			init_pwq(*pwq_p, wq, pool);
-
-			mutex_lock(&wq->mutex);
-			link_pwq(*pwq_p);
-			mutex_unlock(&wq->mutex);
-		}
-		return 0;
-	}
-
-	if (wq->flags & __WQ_ORDERED) {
+		ret = alloc_and_link_percpu_pwqs(wq);
+	} else if (wq->flags & __WQ_ORDERED) {
 		struct pool_workqueue *dfl_pwq;
 
 		ret = apply_workqueue_attrs_locked(wq, ordered_wq_attrs[highpri]);

-- 
2.53.0-Meta


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

* [PATCH 3/6] workqueue: release pwq pools by pool type
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
  2026-07-31 11:57 ` [PATCH 1/6] workqueue: factor out get_percpu_pool() Breno Leitao
  2026-07-31 11:57 ` [PATCH 2/6] workqueue: factor out alloc_and_link_percpu_pwqs() Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  2026-08-03  0:31   ` Tejun Heo
  2026-07-31 11:57 ` [PATCH 4/6] workqueue: account nr_active by the backing pool Breno Leitao
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

pwq_release_workfn() calls put_unbound_pool() based on the WQ_UNBOUND
flag. That works today because an unbound workqueue only ever points at
unbound pools, but the flag is the wrong thing to test: what matters is
whether the pool is a refcounted unbound pool or a permanent per-cpu one.

Add is_pool_cpu_specific() and key the release on it instead of the flag.
This is equivalent for every existing workqueue and stays correct if an
unbound pwq is ever backed by a per-cpu pool. Convert the other
open-coded pool->cpu type checks -- in put_unbound_pool(),
pool_allowed_cpus() and the workqueue watchdog -- to the same helper.

No functional change.

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

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index b386a457c0381..513193be00a7d 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1604,6 +1604,12 @@ work_func_t wq_worker_last_func(struct task_struct *task)
 	return worker->last_func;
 }
 
+/* True if @pool is tied to a specific CPU, rather than an unbound pool. */
+static bool is_pool_cpu_specific(struct worker_pool *pool)
+{
+	return pool->cpu >= 0;
+}
+
 /**
  * wq_node_nr_active - Determine wq_node_nr_active to use
  * @wq: workqueue of interest
@@ -2753,7 +2759,7 @@ static struct worker *alloc_worker(int node)
 
 static cpumask_t *pool_allowed_cpus(struct worker_pool *pool)
 {
-	if (pool->cpu < 0 && pool->attrs->affn_strict)
+	if (!is_pool_cpu_specific(pool) && pool->attrs->affn_strict)
 		return pool->attrs->__pod_cpumask;
 	else
 		return pool->attrs->cpumask;
@@ -5121,7 +5127,7 @@ static void put_unbound_pool(struct worker_pool *pool)
 		return;
 
 	/* sanity checks */
-	if (WARN_ON(!(pool->cpu < 0)) ||
+	if (WARN_ON(is_pool_cpu_specific(pool)) ||
 	    WARN_ON(!list_empty(&pool->worklist)))
 		return;
 
@@ -5273,7 +5279,7 @@ static void pwq_release_workfn(struct kthread_work *work)
 		mutex_unlock(&wq->mutex);
 	}
 
-	if (wq->flags & WQ_UNBOUND) {
+	if (!is_pool_cpu_specific(pool)) {
 		mutex_lock(&wq_pool_mutex);
 		put_unbound_pool(pool);
 		mutex_unlock(&wq_pool_mutex);
@@ -7949,7 +7955,7 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)
 			lockup_detected = true;
 			stall_time = jiffies_to_msecs(now - pool_ts) / 1000;
 			max_stall_time = max(max_stall_time, stall_time);
-			if (pool->cpu >= 0 && !(pool->flags & POOL_BH)) {
+			if (is_pool_cpu_specific(pool) && !(pool->flags & POOL_BH)) {
 				pool->cpu_stall = true;
 				cpu_pool_stall = true;
 			}

-- 
2.53.0-Meta


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

* [PATCH 4/6] workqueue: account nr_active by the backing pool
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
                   ` (2 preceding siblings ...)
  2026-07-31 11:57 ` [PATCH 3/6] workqueue: release pwq pools by pool type Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  2026-08-03  0:34   ` Tejun Heo
  2026-07-31 11:57 ` [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs Breno Leitao
  2026-07-31 11:57 ` [PATCH 6/6] workqueue: install per-cpu pwqs at creation for __WQ_PERCPU_POOLS Breno Leitao
  5 siblings, 1 reply; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

Use is_pool_cpu_specific() instead of wq_node_nr_active() to check for
per workqueues.

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

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 513193be00a7d..d17fdcaaf1685 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1782,13 +1782,16 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill)
 {
 	struct workqueue_struct *wq = pwq->wq;
 	struct worker_pool *pool = pwq->pool;
-	struct wq_node_nr_active *nna = wq_node_nr_active(wq, pool->node);
+	struct wq_node_nr_active *nna;
 	bool obtained = false;
 
 	lockdep_assert_held(&pool->lock);
 
-	if (!nna) {
-		/* BH or per-cpu workqueue, pwq->nr_active is sufficient */
+	/*
+	 * A concurrency-managed per-cpu pool accounts nr_active per pwq, so
+	 * pwq->nr_active against wq->max_active is sufficient.
+	 */
+	if (is_pool_cpu_specific(pool)) {
 		obtained = pwq->nr_active < READ_ONCE(wq->max_active);
 		goto out;
 	}
@@ -1796,6 +1799,10 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill)
 	if (unlikely(pwq->plugged))
 		return false;
 
+	nna = wq_node_nr_active(wq, pool->node);
+	if (WARN_ON_ONCE(!nna))
+		return false;
+
 	/*
 	 * Unbound workqueue uses per-node shared nr_active $nna. If @pwq is
 	 * already waiting on $nna, pwq_dec_nr_active() will maintain the
@@ -2013,7 +2020,7 @@ static void node_activate_pending_pwq(struct wq_node_nr_active *nna,
 static void pwq_dec_nr_active(struct pool_workqueue *pwq)
 {
 	struct worker_pool *pool = pwq->pool;
-	struct wq_node_nr_active *nna = wq_node_nr_active(pwq->wq, pool->node);
+	struct wq_node_nr_active *nna;
 
 	lockdep_assert_held(&pool->lock);
 
@@ -2024,14 +2031,18 @@ static void pwq_dec_nr_active(struct pool_workqueue *pwq)
 	pwq->nr_active--;
 
 	/*
-	 * For a percpu workqueue, it's simple. Just need to kick the first
+	 * A concurrency-managed per-cpu pool only needs to kick the first
 	 * inactive work item on @pwq itself.
 	 */
-	if (!nna) {
+	if (is_pool_cpu_specific(pool)) {
 		pwq_activate_first_inactive(pwq, false);
 		return;
 	}
 
+	nna = wq_node_nr_active(pwq->wq, pool->node);
+	if (WARN_ON_ONCE(!nna))
+		return;
+
 	/*
 	 * If @pwq is for an unbound workqueue, it's more complicated because
 	 * multiple pwqs and pools may be sharing the nr_active count. When a

-- 
2.53.0-Meta


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

* [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
                   ` (3 preceding siblings ...)
  2026-07-31 11:57 ` [PATCH 4/6] workqueue: account nr_active by the backing pool Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  2026-08-03  0:36   ` Tejun Heo
  2026-07-31 11:57 ` [PATCH 6/6] workqueue: install per-cpu pwqs at creation for __WQ_PERCPU_POOLS Breno Leitao
  5 siblings, 1 reply; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

Add alloc_percpu_pwq(), which binds a pwq to get_percpu_pool(wq, cpu),
and an internal __WQ_PERCPU_POOLS flag. unbound_wq_update_pwq() installs
such a pwq per CPU when the flag is set, reusing the existing
install/drain path. Pool release and nr_active are already keyed on the
backing pool, so a per-cpu-backed pwq is torn down and throttled
correctly.

PS: We can do this using if/else for per cpu/unbound as well, instead of
this labels:, would it be better?

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 include/linux/workqueue.h |  1 +
 kernel/workqueue.c        | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index a283766a192aa..5bbbed94d2fa6 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -410,6 +410,7 @@ enum wq_flags {
 	__WQ_ORDERED		= 1 << 17, /* internal: workqueue is ordered */
 	__WQ_LEGACY		= 1 << 18, /* internal: create*_workqueue() */
 	__WQ_DEPRECATED		= 1 << 19, /* internal: workqueue is deprecated */
+	__WQ_PERCPU_POOLS	= 1 << 20, /* internal: back unbound pwqs with percpu pools */
 
 	/* BH wq only allows the following flags */
 	__WQ_BH_ALLOWS		= WQ_BH | WQ_HIGHPRI | WQ_PERCPU,
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index d17fdcaaf1685..df4fc9ccb7b22 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5404,6 +5404,27 @@ static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq,
 	return pwq;
 }
 
+/*
+ * Create a pwq backing @wq on @cpu with the static per-cpu pool instead of a
+ * dedicated unbound pool. Used by the unbound pwq machinery for a workqueue
+ * that requests the per-cpu backend.
+ */
+static struct pool_workqueue *alloc_percpu_pwq(struct workqueue_struct *wq,
+					       int cpu)
+{
+	struct worker_pool *pool = get_percpu_pool(wq, cpu);
+	struct pool_workqueue *pwq;
+
+	lockdep_assert_held(&wq_pool_mutex);
+
+	pwq = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node);
+	if (!pwq)
+		return NULL;
+
+	init_pwq(pwq, wq, pool);
+	return pwq;
+}
+
 /**
  * wq_calc_pod_cpumask - calculate a wq_attrs' cpumask for a pod
  * @attrs: the wq_attrs of the default pwq of the target workqueue
@@ -5643,6 +5664,17 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 	if (!(wq->flags & WQ_UNBOUND) || wq->unbound_attrs->ordered)
 		return;
 
+	if (wq->flags & __WQ_PERCPU_POOLS) {
+		/* nothing to do if @cpu is already backed by its per-cpu pool */
+		if (is_pool_cpu_specific(unbound_pwq(wq, cpu)->pool))
+			return;
+
+		pwq = alloc_percpu_pwq(wq, cpu);
+		if (!pwq)
+			goto use_dfl_pwq;
+		goto install;
+	}
+
 	/*
 	 * We don't wanna alloc/free wq_attrs for each wq for each CPU.
 	 * Let's use a preallocated one.  The following buf is protected by
@@ -5666,6 +5698,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu)
 		goto use_dfl_pwq;
 	}
 
+install:
 	/* Install the new pwq. */
 	mutex_lock(&wq->mutex);
 	old_pwq = install_unbound_pwq(wq, cpu, pwq);

-- 
2.53.0-Meta


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

* [PATCH 6/6] workqueue: install per-cpu pwqs at creation for __WQ_PERCPU_POOLS
  2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
                   ` (4 preceding siblings ...)
  2026-07-31 11:57 ` [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs Breno Leitao
@ 2026-07-31 11:57 ` Breno Leitao
  5 siblings, 0 replies; 15+ messages in thread
From: Breno Leitao @ 2026-07-31 11:57 UTC (permalink / raw)
  To: Tejun Heo, Lai Jiangshan
  Cc: linux-kernel, Breno Leitao, kernel-team, marco.crivellari

alloc_and_link_pwqs() brings up an unbound workqueue with dedicated
unbound pools. For a workqueue that requests the per-cpu backend,
convert those pwqs to per-cpu-backed ones right after creation by
running the per-CPU update, so the backend is in place before the
workqueue is used rather than only after the first CPU hotplug event.

No workqueue sets __WQ_PERCPU_POOLS yet, so there is no functional
change.

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

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index df4fc9ccb7b22..cd3d0d54dfddc 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5766,6 +5766,12 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 
 	if (ret)
 		goto enomem;
+
+	if (wq->flags & __WQ_PERCPU_POOLS) {
+		for_each_possible_cpu(cpu)
+			unbound_wq_update_pwq(wq, cpu);
+	}
+
 	return 0;
 
 enomem:

-- 
2.53.0-Meta


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

* Re: [PATCH 3/6] workqueue: release pwq pools by pool type
  2026-07-31 11:57 ` [PATCH 3/6] workqueue: release pwq pools by pool type Breno Leitao
@ 2026-08-03  0:31   ` Tejun Heo
  2026-08-03 14:02     ` Breno Leitao
  0 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2026-08-03  0:31 UTC (permalink / raw)
  To: Breno Leitao; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

On Fri, Jul 31, 2026 at 04:57:35AM -0700, Breno Leitao wrote:
> pwq_release_workfn() calls put_unbound_pool() based on the WQ_UNBOUND
> flag. That works today because an unbound workqueue only ever points at
> unbound pools, but the flag is the wrong thing to test: what matters is
> whether the pool is a refcounted unbound pool or a permanent per-cpu one.
>
> Add is_pool_cpu_specific() and key the release on it instead of the flag.
> This is equivalent for every existing workqueue and stays correct if an
> unbound pwq is ever backed by a per-cpu pool. Convert the other
> open-coded pool->cpu type checks -- in put_unbound_pool(),
> pool_allowed_cpus() and the workqueue watchdog -- to the same helper.
> 
> No functional change.

The first para saying that it's testing something wrong and then the patch
not having any functional change reads odd. Can you please rewrite?

> +/* True if @pool is tied to a specific CPU, rather than an unbound pool. */
> +static bool is_pool_cpu_specific(struct worker_pool *pool)

I'm not sure about introducing a new term. Can we just stick to percpu?

Thanks.

-- 
tejun

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

* Re: [PATCH 4/6] workqueue: account nr_active by the backing pool
  2026-07-31 11:57 ` [PATCH 4/6] workqueue: account nr_active by the backing pool Breno Leitao
@ 2026-08-03  0:34   ` Tejun Heo
  2026-08-03 14:19     ` Breno Leitao
  0 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2026-08-03  0:34 UTC (permalink / raw)
  To: Breno Leitao; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

Hello,

On Fri, Jul 31, 2026 at 04:57:36AM -0700, Breno Leitao wrote:
> @@ -1796,6 +1799,10 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill)
>  	if (unlikely(pwq->plugged))
>  		return false;
>  
> +	nna = wq_node_nr_active(wq, pool->node);
> +	if (WARN_ON_ONCE(!nna))
> +		return false;

Would it make sense to turn WQ_UNBOUND test in wq_node_nr_active() into a
WARN_ON_ONCE()? And I don't think the return value needs a null check.

Thanks.

-- 
tejun

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

* Re: [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs
  2026-07-31 11:57 ` [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs Breno Leitao
@ 2026-08-03  0:36   ` Tejun Heo
  2026-08-03 16:35     ` Breno Leitao
  0 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2026-08-03  0:36 UTC (permalink / raw)
  To: Breno Leitao; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

Hello,

On Fri, Jul 31, 2026 at 04:57:37AM -0700, Breno Leitao wrote:
> Add alloc_percpu_pwq(), which binds a pwq to get_percpu_pool(wq, cpu),
> and an internal __WQ_PERCPU_POOLS flag. unbound_wq_update_pwq() installs
> such a pwq per CPU when the flag is set, reusing the existing
> install/drain path. Pool release and nr_active are already keyed on the
> backing pool, so a per-cpu-backed pwq is torn down and throttled
> correctly.
> 
> PS: We can do this using if/else for per cpu/unbound as well, instead of
> this labels:, would it be better?

So, we would have percpu-pool backed unbound workqueues in addition to the
existing percpu workqueues? I'm not sure that makes sense. What prevents
unifying them?

Thanks.

-- 
tejun

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

* Re: [PATCH 3/6] workqueue: release pwq pools by pool type
  2026-08-03  0:31   ` Tejun Heo
@ 2026-08-03 14:02     ` Breno Leitao
  2026-08-03 16:50       ` Tejun Heo
  0 siblings, 1 reply; 15+ messages in thread
From: Breno Leitao @ 2026-08-03 14:02 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

On Sun, Aug 02, 2026 at 02:31:27PM -1000, Tejun Heo wrote:
> On Fri, Jul 31, 2026 at 04:57:35AM -0700, Breno Leitao wrote:
> > pwq_release_workfn() calls put_unbound_pool() based on the WQ_UNBOUND
> > flag. That works today because an unbound workqueue only ever points at
> > unbound pools, but the flag is the wrong thing to test: what matters is
> > whether the pool is a refcounted unbound pool or a permanent per-cpu one.
> >
> > Add is_pool_cpu_specific() and key the release on it instead of the flag.
> > This is equivalent for every existing workqueue and stays correct if an
> > unbound pwq is ever backed by a per-cpu pool. Convert the other
> > open-coded pool->cpu type checks -- in put_unbound_pool(),
> > pool_allowed_cpus() and the workqueue watchdog -- to the same helper.
> > 
> > No functional change.
> 
> The first para saying that it's testing something wrong and then the patch
> not having any functional change reads odd. Can you please rewrite?

Fair, I meant that  the two conditions agree today and nothing changes
here. I will get this better.

> > +/* True if @pool is tied to a specific CPU, rather than an unbound
> > pool. */ +static bool is_pool_cpu_specific(struct worker_pool *pool)
> 
> I'm not sure about introducing a new term. Can we just stick to
> percpu?

Sure -- is_percpu_pool() any better?

Thanks,
--breno

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

* Re: [PATCH 4/6] workqueue: account nr_active by the backing pool
  2026-08-03  0:34   ` Tejun Heo
@ 2026-08-03 14:19     ` Breno Leitao
  0 siblings, 0 replies; 15+ messages in thread
From: Breno Leitao @ 2026-08-03 14:19 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

On Sun, Aug 02, 2026 at 02:34:16PM -1000, Tejun Heo wrote:
> Hello,
> 
> On Fri, Jul 31, 2026 at 04:57:36AM -0700, Breno Leitao wrote:
> > @@ -1796,6 +1799,10 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill)
> >  	if (unlikely(pwq->plugged))
> >  		return false;
> >  
> > +	nna = wq_node_nr_active(wq, pool->node);
> > +	if (WARN_ON_ONCE(!nna))
> > +		return false;
> 
> Would it make sense to turn WQ_UNBOUND test in wq_node_nr_active() into a
> WARN_ON_ONCE()? And I don't think the return value needs a null check.

Yes, that's the right place for it. wq->node_nr_active[] is only makes
sense for WQ_UNBOUND, so a NULL return was always a caller bug rather than an
answer.

I will improve it on v2.

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

* Re: [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs
  2026-08-03  0:36   ` Tejun Heo
@ 2026-08-03 16:35     ` Breno Leitao
  2026-08-03 16:58       ` Tejun Heo
  0 siblings, 1 reply; 15+ messages in thread
From: Breno Leitao @ 2026-08-03 16:35 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

On Sun, Aug 02, 2026 at 02:36:52PM -1000, Tejun Heo wrote:
> Hello,
> 
> On Fri, Jul 31, 2026 at 04:57:37AM -0700, Breno Leitao wrote:
> > Add alloc_percpu_pwq(), which binds a pwq to get_percpu_pool(wq, cpu),
> > and an internal __WQ_PERCPU_POOLS flag. unbound_wq_update_pwq() installs
> > such a pwq per CPU when the flag is set, reusing the existing
> > install/drain path. Pool release and nr_active are already keyed on the
> > backing pool, so a per-cpu-backed pwq is torn down and throttled
> > correctly.
> > 
> > PS: We can do this using if/else for per cpu/unbound as well, instead of
> > this labels:, would it be better?
> 
> So, we would have percpu-pool backed unbound workqueues in addition to the
> existing percpu workqueues? I'm not sure that makes sense. What prevents
> unifying them?

Nothing fundamental. What I'm working out here is how to plug the unbound
pwq machinery into the percpu pools, and I came up with this
__WQ_PERCPU_POOLS.

I am trying to following what you said here:
described in https://lore.kernel.org/all/ak569WYSm3ygKl1-@slm.duckdns.org :

	> [...] just merge percpu and unbound workqueues. Unbound workqueues
	> already have the ability to update pwqs (or rather install new ones and
	> drain old ones), which is how attribute changes are implemented. If we
	> make the pwqs be able to point to both unbound and percpu pools, the
	> dynamic switch falls out naturally and percpu just becomes one of the
	> affinity settings.

This is the design I am trying to convey to, please let me know if they
are not correct.

  a) There is one workqueue implementation -- what is today the unbound one --
     and percpu becomes one of its affinity settings rather than a separate
     type. The unbound_* helper names get renamed/removed once they stop
     being unbound-specific. So, `alloc_unbound_pwq()` becomes
     `alloc_pwq` and handle both cases.

  b) There is one pwq path, and alloc_and_link_percpu_pwqs() goes away.

  c) Which pool backs a pwq becomes a property of the attrs, resolved in
     alloc_unbound_pwq(), which hands out either the static percpu pool for
     that CPU or a hashed unbound one.

  d) Both pool types stay -- concurrency management only exists on the percpu
     ones -- so what gets merged is the machinery, not the pools.

  e) this ugly __WQ_PERCPU_POOLS goes away, WQ_PERCPU carries the
     intent, and the unbound path handles WQ_PERCPU directly. WQ_PERCPU
     and WQ_UNBOUND stop being mutually exclusive types and become
     a choice of backing, so the exclusivity check in alloc_workqueue()
     changes meaning or goes away.

Thanks for the guidance here,
--breno

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

* Re: [PATCH 3/6] workqueue: release pwq pools by pool type
  2026-08-03 14:02     ` Breno Leitao
@ 2026-08-03 16:50       ` Tejun Heo
  0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2026-08-03 16:50 UTC (permalink / raw)
  To: Breno Leitao; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

Hello,

On Mon, Aug 03, 2026 at 07:02:46AM -0700, Breno Leitao wrote:
> > > +/* True if @pool is tied to a specific CPU, rather than an unbound
> > > pool. */ +static bool is_pool_cpu_specific(struct worker_pool *pool)
> > 
> > I'm not sure about introducing a new term. Can we just stick to
> > percpu?
> 
> Sure -- is_percpu_pool() any better?

Yeah, that sounds fine to me.

Thanks.

-- 
tejun

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

* Re: [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs
  2026-08-03 16:35     ` Breno Leitao
@ 2026-08-03 16:58       ` Tejun Heo
  0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2026-08-03 16:58 UTC (permalink / raw)
  To: Breno Leitao; +Cc: Lai Jiangshan, linux-kernel, kernel-team, marco.crivellari

On Mon, Aug 03, 2026 at 09:35:16AM -0700, Breno Leitao wrote:
>   a) There is one workqueue implementation -- what is today the unbound one --
>      and percpu becomes one of its affinity settings rather than a separate
>      type. The unbound_* helper names get renamed/removed once they stop
>      being unbound-specific. So, `alloc_unbound_pwq()` becomes
>      `alloc_pwq` and handle both cases.
> 
>   b) There is one pwq path, and alloc_and_link_percpu_pwqs() goes away.
> 
>   c) Which pool backs a pwq becomes a property of the attrs, resolved in
>      alloc_unbound_pwq(), which hands out either the static percpu pool for
>      that CPU or a hashed unbound one.
> 
>   d) Both pool types stay -- concurrency management only exists on the percpu
>      ones -- so what gets merged is the machinery, not the pools.
> 
>   e) this ugly __WQ_PERCPU_POOLS goes away, WQ_PERCPU carries the
>      intent, and the unbound path handles WQ_PERCPU directly. WQ_PERCPU
>      and WQ_UNBOUND stop being mutually exclusive types and become
>      a choice of backing, so the exclusivity check in alloc_workqueue()
>      changes meaning or goes away.

Yeah, this makes sense to me.

Thanks.

-- 
tejun

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

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

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 11:57 [PATCH 0/6] workqueue: base pwq pool release and nr_active on the backing pool Breno Leitao
2026-07-31 11:57 ` [PATCH 1/6] workqueue: factor out get_percpu_pool() Breno Leitao
2026-07-31 11:57 ` [PATCH 2/6] workqueue: factor out alloc_and_link_percpu_pwqs() Breno Leitao
2026-07-31 11:57 ` [PATCH 3/6] workqueue: release pwq pools by pool type Breno Leitao
2026-08-03  0:31   ` Tejun Heo
2026-08-03 14:02     ` Breno Leitao
2026-08-03 16:50       ` Tejun Heo
2026-07-31 11:57 ` [PATCH 4/6] workqueue: account nr_active by the backing pool Breno Leitao
2026-08-03  0:34   ` Tejun Heo
2026-08-03 14:19     ` Breno Leitao
2026-07-31 11:57 ` [PATCH 5/6] workqueue: add a per-cpu backend for unbound pwqs Breno Leitao
2026-08-03  0:36   ` Tejun Heo
2026-08-03 16:35     ` Breno Leitao
2026-08-03 16:58       ` Tejun Heo
2026-07-31 11:57 ` [PATCH 6/6] workqueue: install per-cpu pwqs at creation for __WQ_PERCPU_POOLS Breno Leitao

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