From: Wilson Felipe Pereira <wfelipe@google.com>
To: Tejun Heo <tj@kernel.org>, Lai Jiangshan <jiangshanlai@gmail.com>
Cc: Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
Wilson Felipe Pereira <wfelipe@google.com>,
Shuhao Tan <tanshuhao@google.com>
Subject: [PATCH 2/3] workqueue: expose per-workqueue stats in debugfs
Date: Thu, 22 Oct 2026 15:53:35 +0000 [thread overview]
Message-ID: <20261022155352.1293020-3-wfelipe@google.com> (raw)
In-Reply-To: <20261022155352.1293020-1-wfelipe@google.com>
struct pool_workqueue tracks runtime counters in pwq->stats[],
pwq->total_in_flight, and pwq->nr_active, but reading them currently
requires tools/workqueue/wq_monitor.py with drgn and kernel DWARF
debuginfo. This makes it impractical to inspect workqueue stats on
systems without drgn or debug symbols.
Add /sys/kernel/debug/workqueue/stats to expose per-workqueue stats
directly from the kernel. Iterate over the workqueues list under RCU and
sum wq->retired_stats[] with each pwq's stats[], total_in_flight, and
nr_active to report queued, active, and running work items alongside the
cumulative pwq->stats[] counters.
Signed-off-by: Wilson Felipe Pereira <wfelipe@google.com>
---
kernel/workqueue.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 88c8cc2b38d8..ed0f0be6494a 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -57,6 +57,8 @@
#include <linux/delay.h>
#include <linux/irq_work.h>
#include <linux/math64.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include "workqueue_internal.h"
@@ -7990,6 +7992,103 @@ static void workqueue_sysfs_unregister(struct workqueue_struct *wq)
static void workqueue_sysfs_unregister(struct workqueue_struct *wq) { }
#endif /* CONFIG_SYSFS */
+#ifdef CONFIG_DEBUG_FS
+
+static const char *wq_type_str(struct workqueue_struct *wq)
+{
+ if (wq->flags & WQ_BH)
+ return "bh";
+ if (wq->flags & WQ_UNBOUND) {
+ if (wq->flags & __WQ_ORDERED)
+ return "ordered";
+ return "unbound";
+ }
+ return "percpu";
+}
+
+static void *wq_stats_start(struct seq_file *m, loff_t *pos)
+ __acquires(rcu)
+{
+ rcu_read_lock();
+ return seq_list_start_head_rcu(&workqueues, *pos);
+}
+
+static void *wq_stats_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ return seq_list_next_rcu(v, &workqueues, pos);
+}
+
+static void wq_stats_stop(struct seq_file *m, void *v)
+ __releases(rcu)
+{
+ rcu_read_unlock();
+}
+
+static int wq_stats_show(struct seq_file *m, void *v)
+{
+ struct workqueue_struct *wq;
+ struct pool_workqueue *pwq;
+ u64 stats[PWQ_NR_STATS];
+ long in_flight = 0, active = 0;
+ long running, queued;
+ int i;
+
+ if (v == &workqueues) {
+ seq_printf(m, "%-24s %-8s %6s %6s %7s %10s %10s %12s %8s %8s %8s %8s %8s\n",
+ "workqueue", "type", "queued", "active", "running",
+ "started", "completed", "cpu_time_us",
+ "cpu_itsv", "cm_wkup", "repat", "mayday", "rescued");
+ return 0;
+ }
+
+ wq = list_entry(v, struct workqueue_struct, list);
+
+ /* Advisory stats read without wq->mutex or pool->lock. */
+ for (i = 0; i < PWQ_NR_STATS; i++)
+ stats[i] = data_race(wq->retired_stats[i]);
+
+ for_each_pwq(pwq, wq) {
+ for (i = 0; i < PWQ_NR_STATS; i++)
+ stats[i] += data_race(pwq->stats[i]);
+ in_flight += data_race(pwq->total_in_flight);
+ active += data_race(pwq->nr_active);
+ }
+
+ running = max_t(long, stats[PWQ_STAT_STARTED] -
+ stats[PWQ_STAT_COMPLETED], 0);
+ queued = max_t(long, in_flight - running, 0);
+ active = max_t(long, active, 0);
+
+ seq_printf(m, "%-24s %-8s %6ld %6ld %7ld %10llu %10llu %12llu %8llu %8llu %8llu %8llu %8llu\n",
+ wq->name, wq_type_str(wq), queued, active, running,
+ stats[PWQ_STAT_STARTED], stats[PWQ_STAT_COMPLETED],
+ stats[PWQ_STAT_CPU_TIME], stats[PWQ_STAT_CPU_INTENSIVE],
+ stats[PWQ_STAT_CM_WAKEUP], stats[PWQ_STAT_REPATRIATED],
+ stats[PWQ_STAT_MAYDAY], stats[PWQ_STAT_RESCUED]);
+
+ return 0;
+}
+
+static const struct seq_operations wq_stats_sops = {
+ .start = wq_stats_start,
+ .next = wq_stats_next,
+ .stop = wq_stats_stop,
+ .show = wq_stats_show,
+};
+DEFINE_SEQ_ATTRIBUTE(wq_stats);
+
+static int __init wq_debugfs_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("workqueue", NULL);
+ debugfs_create_file("stats", 0444, dir, NULL, &wq_stats_fops);
+ return 0;
+}
+late_initcall(wq_debugfs_init);
+
+#endif /* CONFIG_DEBUG_FS */
+
/*
* Workqueue watchdog.
*
--
2.56.0.218.g1ec904b173-goog
next prev parent reply other threads:[~2026-10-22 15:54 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 15:53 [PATCH 0/3] " Wilson Felipe Pereira
2026-10-22 15:53 ` [PATCH 1/3] workqueue: preserve pwq->stats[] when unbound pwqs are released Wilson Felipe Pereira
2026-10-22 15:53 ` Wilson Felipe Pereira [this message]
2026-10-22 15:53 ` [PATCH 3/3] tools/workqueue: report queued items and retired stats in wq_monitor.py Wilson Felipe Pereira
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261022155352.1293020-3-wfelipe@google.com \
--to=wfelipe@google.com \
--cc=corbet@lwn.net \
--cc=jiangshanlai@gmail.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
--cc=tanshuhao@google.com \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®