Path: blob/master/samples/workqueue/stall_detector/wq_stall.c
121817 views
// SPDX-License-Identifier: GPL-2.01/*2* wq_stall - Test module for the workqueue stall detector.3*4* Deliberately creates a workqueue stall so the watchdog fires and5* prints diagnostic output. Useful for verifying that the stall6* detector correctly identifies stuck workers and produces useful7* backtraces.8*9* The stall is triggered by clearing PF_WQ_WORKER before sleeping,10* which hides the worker from the concurrency manager. A second11* work item queued on the same pool then sits in the worklist with12* no worker available to process it.13*14* After ~30s the workqueue watchdog fires:15* BUG: workqueue lockup - pool cpus=N ...16*17* Build:18* make -C <kernel tree> M=samples/workqueue/stall_detector modules19*20* Copyright (c) 2026 Meta Platforms, Inc. and affiliates.21* Copyright (c) 2026 Breno Leitao <[email protected]>22*/2324#include <linux/module.h>25#include <linux/workqueue.h>26#include <linux/wait.h>27#include <linux/atomic.h>28#include <linux/sched.h>2930static DECLARE_WAIT_QUEUE_HEAD(stall_wq_head);31static atomic_t wake_condition = ATOMIC_INIT(0);32static struct work_struct stall_work1;33static struct work_struct stall_work2;3435static void stall_work2_fn(struct work_struct *work)36{37pr_info("wq_stall: second work item finally ran\n");38}3940static void stall_work1_fn(struct work_struct *work)41{42pr_info("wq_stall: first work item running on cpu %d\n",43raw_smp_processor_id());4445/*46* Queue second item while we're still counted as running47* (pool->nr_running > 0). Since schedule_work() on a per-CPU48* workqueue targets raw_smp_processor_id(), item 2 lands on the49* same pool. __queue_work -> kick_pool -> need_more_worker()50* sees nr_running > 0 and does NOT wake a new worker.51*/52schedule_work(&stall_work2);5354/*55* Hide from the workqueue concurrency manager. Without56* PF_WQ_WORKER, schedule() won't call wq_worker_sleeping(),57* so nr_running is never decremented and no replacement58* worker is created. Item 2 stays stuck in pool->worklist.59*/60current->flags &= ~PF_WQ_WORKER;6162pr_info("wq_stall: entering wait_event_idle (PF_WQ_WORKER cleared)\n");63pr_info("wq_stall: expect 'BUG: workqueue lockup' in ~30-60s\n");64wait_event_idle(stall_wq_head, atomic_read(&wake_condition) != 0);6566/* Restore so process_one_work() cleanup works correctly */67current->flags |= PF_WQ_WORKER;68pr_info("wq_stall: woke up, PF_WQ_WORKER restored\n");69}7071static int __init wq_stall_init(void)72{73pr_info("wq_stall: loading\n");7475INIT_WORK(&stall_work1, stall_work1_fn);76INIT_WORK(&stall_work2, stall_work2_fn);77schedule_work(&stall_work1);7879return 0;80}8182static void __exit wq_stall_exit(void)83{84pr_info("wq_stall: unloading\n");85atomic_set(&wake_condition, 1);86wake_up(&stall_wq_head);87flush_work(&stall_work1);88flush_work(&stall_work2);89pr_info("wq_stall: all work flushed, module unloaded\n");90}9192module_init(wq_stall_init);93module_exit(wq_stall_exit);9495MODULE_LICENSE("GPL");96MODULE_DESCRIPTION("Reproduce workqueue stall caused by PF_WQ_WORKER misuse");97MODULE_AUTHOR("Breno Leitao <[email protected]>");9899100