[PATCH v2 2/3] workqueue: Add workqueue_mayday and workqueue_rescued tracepoints
From: Aaron Tomlin <atomlin@atomlin.com>
Date: 2026-09-04 19:35:00
Also in:
lkml
Subsystem:
the rest, tracing, workqueue · Maintainers:
Linus Torvalds, Steven Rostedt, Masami Hiramatsu, Tejun Heo
When a worker pool fails to create a new worker thread within MAYDAY_INTERVAL (e.g., typically under severe memory pressure where memory reclaim paths depend on pending work items), send_mayday() signals distress to the workqueue's rescuer thread. The rescuer then takes over processing the pending work items via assign_rescuer_work(). While pwq->stats[PWQ_STAT_MAYDAY] and pwq->stats[PWQ_STAT_RESCUED] track these occurrences cumulatively, there is currently no event-driven mechanism to observe exactly when mayday distress occurs or which work items require rescue. Add two new tracepoints namely workqueue_mayday and workqueue_rescued to make distress and rescuer execution easily observable (e.g., via Ftrace or eBPF). Signed-off-by: Aaron Tomlin <atomlin@atomlin.com> --- include/trace/events/workqueue.h | 68 ++++++++++++++++++++++++++++++++ kernel/workqueue.c | 2 + 2 files changed, 70 insertions(+)
diff --git a/include/trace/events/workqueue.h b/include/trace/events/workqueue.h
index d2ad38f93a83..12ac24586835 100644
--- a/include/trace/events/workqueue.h
+++ b/include/trace/events/workqueue.h@@ -165,6 +165,74 @@ TRACE_EVENT(workqueue_cpu_intensive, __entry->cpu, __entry->duration_us) ); +/** + * workqueue_mayday - called when a pool_workqueue sends mayday to rescuer + * @pwq: pointer to struct pool_workqueue + * + * This event occurs when a worker pool fails to create a new worker + * within MAYDAY_INTERVAL and requests the workqueue's rescuer thread to + * process pending works. + */ +TRACE_EVENT(workqueue_mayday, + + TP_PROTO(struct pool_workqueue *pwq), + + TP_ARGS(pwq), + + TP_STRUCT__entry( + __string( workqueue, pwq->wq->name ) + __field( int, pool_id ) + __field( int, cpu ) + __field( int, nr_active ) + ), + + TP_fast_assign( + __assign_str(workqueue); + __entry->pool_id = pwq->pool->id; + __entry->cpu = pwq->pool->cpu; + __entry->nr_active = pwq->nr_active; + ), + + TP_printk("workqueue=%s pool_id=%d cpu=%d nr_active=%d", + __get_str(workqueue), __entry->pool_id, __entry->cpu, + __entry->nr_active) +); + +/** + * workqueue_rescued - called when a work item is assigned to a rescuer + * @pwq: pointer to struct pool_workqueue + * @work: pointer to struct work_struct + * @function: pointer to worker function + * + * This event occurs when a work item is claimed by a rescuer thread + * to guarantee forward progress. + */ +TRACE_EVENT(workqueue_rescued, + + TP_PROTO(struct pool_workqueue *pwq, struct work_struct *work, + work_func_t function), + + TP_ARGS(pwq, work, function), + + TP_STRUCT__entry( + __field( void *, work ) + __field( void *, function ) + __string( workqueue, pwq->wq->name ) + __field( int, cpu ) + ), + + TP_fast_assign( + __entry->work = work; + __entry->function = function; + __assign_str(workqueue); + __entry->cpu = pwq->pool->cpu; + ), + + TP_printk("work struct=%p function=%ps workqueue=%s cpu=%d", + __entry->work, __entry->function, __get_str(workqueue), + __entry->cpu) +); + #endif /* _TRACE_WORKQUEUE_H */ /* This part must be outside protection */
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index bad1217cf45a..ab1371ed890a 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c@@ -3124,6 +3124,7 @@ static void send_mayday(struct pool_workqueue *pwq) list_add_tail(&pwq->mayday_node, &wq->maydays); wake_up_process(wq->rescuer->task); pwq->stats[PWQ_STAT_MAYDAY]++; + trace_workqueue_mayday(pwq); } }
@@ -3621,6 +3622,7 @@ static bool assign_rescuer_work(struct pool_workqueue *pwq, struct worker *rescu list_for_each_entry_safe_from(work, n, &pool->worklist, entry) { if (get_work_pwq(work) == pwq && assign_work(work, rescuer, &n)) { pwq->stats[PWQ_STAT_RESCUED]++; + trace_workqueue_rescued(pwq, work, work->func); /* put the cursor for next search */ list_move_tail(&cursor->entry, &n->entry); return true;
--
2.55.0