From bda345b3fc59f2899c50379c64a0734a3e720683 Mon Sep 17 00:00:00 2001 From: Nguyen Ngoc Thang Date: Wed, 14 Oct 2026 22:43:41 +0700 Subject: workqueue: mark pool->nr_running as __data_racy wq_worker_running() increments pool->nr_running with only preemption disabled, while kick_pool_pick() reads it via need_more_worker() under pool->lock, so the two can run concurrently on different CPUs. KCSAN reports: BUG: KCSAN: data-race in kick_pool_pick / wq_worker_running write to 0xffff888059c2af24 of 4 bytes by task 3007 on cpu 0: wq_worker_running+0x114/0x130 schedule_timeout+0x16e/0x180 io_schedule_timeout+0x4c/0x70 submit_bio_wait+0xd7/0x110 blkdev_issue_flush+0x4c/0x70 ext4_sync_file+0x60d/0x6c0 aio_fsync_work+0x7f/0x270 process_one_work+0x4ae/0xa00 read to 0xffff888059c2af24 of 4 bytes by task 3001 on cpu 1: kick_pool_pick+0x7a/0x3a0 __queue_work+0x72d/0xcf0 queue_work_on+0x9d/0xb0 schedule_on_each_cpu+0x11a/0x2d0 vmstat_refresh+0x23/0x140 proc_sys_call_handler+0x2b9/0x4f0 The race is benign. The increment in wq_worker_running() is the only lockless write to nr_running; decrements and the reset are done under pool->lock, so a reader holding the lock sees either the old or the new count. A stale 0 costs at most a spurious wakeup of an idle worker. A nonzero count is correct because the incrementing worker is running and re-checks the worklist under pool->lock before it stops counting. The increment can also go 1 -> 2, e.g. when a worker sleeping in a work item wakes up after kick_pool() woke another worker. The lockless increment is part of how nr_running is designed to work, as the comment above the field documents. Mark the field __data_racy so that every reader is covered, not just need_more_worker(). Reported-by: syzbot+8922e6b8343f70be92e8@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8922e6b8343f70be92e8 Signed-off-by: Nguyen Ngoc Thang Signed-off-by: Tejun Heo --- kernel/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/workqueue.c') diff --git a/kernel/workqueue.c b/kernel/workqueue.c index e618108c6127..41754f4126a6 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -212,7 +212,7 @@ struct worker_pool { * but w/ pool->lock held. The readers grab pool->lock and are * guaranteed to see if the counter reached zero. */ - int nr_running; + int __data_racy nr_running; struct list_head worklist; /* L: list of pending works */ -- cgit v1.2.3