[PATCH 1/3] workqueue: preserve pwq->stats[] when unbound pwqs are released

From: Wilson Felipe Pereira

Date: Thu Oct 22 2026 - 11:56:51 EST


When an unbound pool_workqueue is replaced (e.g. during CPU hotplug or
apply_workqueue_attrs()), pwq_release_workfn() unlinks and frees the old
pwq along with its pwq->stats[]. This causes cumulative per-workqueue
counters to go backwards.

Add retired_stats[] to struct workqueue_struct and accumulate a released
pwq's stats into wq->retired_stats[] under wq->mutex before freeing it.

Signed-off-by: Wilson Felipe Pereira <wfelipe@xxxxxxxxxx>
---
kernel/workqueue.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index f2e8071a6f4e..88c8cc2b38d8 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -397,6 +397,8 @@ struct workqueue_struct {
struct workqueue_attrs *attrs; /* PW: workqueue attributes */
struct pool_workqueue __rcu *dfl_pwq; /* PW: only for unbound wqs */

+ u64 retired_stats[PWQ_NR_STATS]; /* WQ: stats from released pwqs */
+
#ifdef CONFIG_SYSFS
struct wq_device *wq_dev; /* I: for sysfs interface */
#endif
@@ -5475,8 +5477,12 @@ static void pwq_release_workfn(struct kthread_work *work)
* @wq, and @wq is invalid to access.
*/
if (!list_empty(&pwq->pwqs_node)) {
+ int i;
+
mutex_lock(&wq->mutex);
list_del_rcu(&pwq->pwqs_node);
+ for (i = 0; i < PWQ_NR_STATS; i++)
+ wq->retired_stats[i] += pwq->stats[i];
is_last = list_empty(&wq->pwqs);

/*
--
2.56.0.218.g1ec904b173-goog