Skip to content

Commit 8a56530

Browse files
leitaoaxboe
authored andcommitted
io_uring/io-wq: Use set_bit() and test_bit() at worker->flags
Utilize set_bit() and test_bit() on worker->flags within io_uring/io-wq to address potential data races. The structure io_worker->flags may be accessed through various data paths, leading to concurrency issues. When KCSAN is enabled, it reveals data races occurring in io_worker_handle_work and io_wq_activate_free_worker functions. BUG: KCSAN: data-race in io_worker_handle_work / io_wq_activate_free_worker write to 0xffff8885c4246404 of 4 bytes by task 49071 on cpu 28: io_worker_handle_work (io_uring/io-wq.c:434 io_uring/io-wq.c:569) io_wq_worker (io_uring/io-wq.c:?) <snip> read to 0xffff8885c4246404 of 4 bytes by task 49024 on cpu 5: io_wq_activate_free_worker (io_uring/io-wq.c:? io_uring/io-wq.c:285) io_wq_enqueue (io_uring/io-wq.c:947) io_queue_iowq (io_uring/io_uring.c:524) io_req_task_submit (io_uring/io_uring.c:1511) io_handle_tw_list (io_uring/io_uring.c:1198) <snip> Line numbers against commit 18daea7 ("Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm"). These races involve writes and reads to the same memory location by different tasks running on different CPUs. To mitigate this, refactor the code to use atomic operations such as set_bit(), test_bit(), and clear_bit() instead of basic "and" and "or" operations. This ensures thread-safe manipulation of worker flags. Also, move `create_index` to avoid holes in the structure. Signed-off-by: Breno Leitao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent 59b28a6 commit 8a56530

File tree

1 file changed

+24
-23
lines changed

1 file changed

+24
-23
lines changed

io_uring/io-wq.c

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
#define WORKER_IDLE_TIMEOUT (5 * HZ)
2626

2727
enum {
28-
IO_WORKER_F_UP = 1, /* up and active */
29-
IO_WORKER_F_RUNNING = 2, /* account as running */
30-
IO_WORKER_F_FREE = 4, /* worker on free list */
31-
IO_WORKER_F_BOUND = 8, /* is doing bounded work */
28+
IO_WORKER_F_UP = 0, /* up and active */
29+
IO_WORKER_F_RUNNING = 1, /* account as running */
30+
IO_WORKER_F_FREE = 2, /* worker on free list */
31+
IO_WORKER_F_BOUND = 3, /* is doing bounded work */
3232
};
3333

3434
enum {
@@ -44,7 +44,8 @@ enum {
4444
*/
4545
struct io_worker {
4646
refcount_t ref;
47-
unsigned flags;
47+
int create_index;
48+
unsigned long flags;
4849
struct hlist_nulls_node nulls_node;
4950
struct list_head all_list;
5051
struct task_struct *task;
@@ -57,7 +58,6 @@ struct io_worker {
5758

5859
unsigned long create_state;
5960
struct callback_head create_work;
60-
int create_index;
6161

6262
union {
6363
struct rcu_head rcu;
@@ -164,7 +164,7 @@ static inline struct io_wq_acct *io_work_get_acct(struct io_wq *wq,
164164

165165
static inline struct io_wq_acct *io_wq_get_acct(struct io_worker *worker)
166166
{
167-
return io_get_acct(worker->wq, worker->flags & IO_WORKER_F_BOUND);
167+
return io_get_acct(worker->wq, test_bit(IO_WORKER_F_BOUND, &worker->flags));
168168
}
169169

170170
static void io_worker_ref_put(struct io_wq *wq)
@@ -224,7 +224,7 @@ static void io_worker_exit(struct io_worker *worker)
224224
wait_for_completion(&worker->ref_done);
225225

226226
raw_spin_lock(&wq->lock);
227-
if (worker->flags & IO_WORKER_F_FREE)
227+
if (test_bit(IO_WORKER_F_FREE, &worker->flags))
228228
hlist_nulls_del_rcu(&worker->nulls_node);
229229
list_del_rcu(&worker->all_list);
230230
raw_spin_unlock(&wq->lock);
@@ -409,7 +409,7 @@ static void io_wq_dec_running(struct io_worker *worker)
409409
struct io_wq_acct *acct = io_wq_get_acct(worker);
410410
struct io_wq *wq = worker->wq;
411411

412-
if (!(worker->flags & IO_WORKER_F_UP))
412+
if (!test_bit(IO_WORKER_F_UP, &worker->flags))
413413
return;
414414

415415
if (!atomic_dec_and_test(&acct->nr_running))
@@ -429,8 +429,8 @@ static void io_wq_dec_running(struct io_worker *worker)
429429
*/
430430
static void __io_worker_busy(struct io_wq *wq, struct io_worker *worker)
431431
{
432-
if (worker->flags & IO_WORKER_F_FREE) {
433-
worker->flags &= ~IO_WORKER_F_FREE;
432+
if (test_bit(IO_WORKER_F_FREE, &worker->flags)) {
433+
clear_bit(IO_WORKER_F_FREE, &worker->flags);
434434
raw_spin_lock(&wq->lock);
435435
hlist_nulls_del_init_rcu(&worker->nulls_node);
436436
raw_spin_unlock(&wq->lock);
@@ -443,8 +443,8 @@ static void __io_worker_busy(struct io_wq *wq, struct io_worker *worker)
443443
static void __io_worker_idle(struct io_wq *wq, struct io_worker *worker)
444444
__must_hold(wq->lock)
445445
{
446-
if (!(worker->flags & IO_WORKER_F_FREE)) {
447-
worker->flags |= IO_WORKER_F_FREE;
446+
if (!test_bit(IO_WORKER_F_FREE, &worker->flags)) {
447+
set_bit(IO_WORKER_F_FREE, &worker->flags);
448448
hlist_nulls_add_head_rcu(&worker->nulls_node, &wq->free_list);
449449
}
450450
}
@@ -632,7 +632,8 @@ static int io_wq_worker(void *data)
632632
bool exit_mask = false, last_timeout = false;
633633
char buf[TASK_COMM_LEN];
634634

635-
worker->flags |= (IO_WORKER_F_UP | IO_WORKER_F_RUNNING);
635+
set_mask_bits(&worker->flags, 0,
636+
BIT(IO_WORKER_F_UP) | BIT(IO_WORKER_F_RUNNING));
636637

637638
snprintf(buf, sizeof(buf), "iou-wrk-%d", wq->task->pid);
638639
set_task_comm(current, buf);
@@ -696,11 +697,11 @@ void io_wq_worker_running(struct task_struct *tsk)
696697

697698
if (!worker)
698699
return;
699-
if (!(worker->flags & IO_WORKER_F_UP))
700+
if (!test_bit(IO_WORKER_F_UP, &worker->flags))
700701
return;
701-
if (worker->flags & IO_WORKER_F_RUNNING)
702+
if (test_bit(IO_WORKER_F_RUNNING, &worker->flags))
702703
return;
703-
worker->flags |= IO_WORKER_F_RUNNING;
704+
set_bit(IO_WORKER_F_RUNNING, &worker->flags);
704705
io_wq_inc_running(worker);
705706
}
706707

@@ -714,12 +715,12 @@ void io_wq_worker_sleeping(struct task_struct *tsk)
714715

715716
if (!worker)
716717
return;
717-
if (!(worker->flags & IO_WORKER_F_UP))
718+
if (!test_bit(IO_WORKER_F_UP, &worker->flags))
718719
return;
719-
if (!(worker->flags & IO_WORKER_F_RUNNING))
720+
if (!test_bit(IO_WORKER_F_RUNNING, &worker->flags))
720721
return;
721722

722-
worker->flags &= ~IO_WORKER_F_RUNNING;
723+
clear_bit(IO_WORKER_F_RUNNING, &worker->flags);
723724
io_wq_dec_running(worker);
724725
}
725726

@@ -733,7 +734,7 @@ static void io_init_new_worker(struct io_wq *wq, struct io_worker *worker,
733734
raw_spin_lock(&wq->lock);
734735
hlist_nulls_add_head_rcu(&worker->nulls_node, &wq->free_list);
735736
list_add_tail_rcu(&worker->all_list, &wq->all_list);
736-
worker->flags |= IO_WORKER_F_FREE;
737+
set_bit(IO_WORKER_F_FREE, &worker->flags);
737738
raw_spin_unlock(&wq->lock);
738739
wake_up_new_task(tsk);
739740
}
@@ -839,7 +840,7 @@ static bool create_io_worker(struct io_wq *wq, int index)
839840
init_completion(&worker->ref_done);
840841

841842
if (index == IO_WQ_ACCT_BOUND)
842-
worker->flags |= IO_WORKER_F_BOUND;
843+
set_bit(IO_WORKER_F_BOUND, &worker->flags);
843844

844845
tsk = create_io_thread(io_wq_worker, worker, NUMA_NO_NODE);
845846
if (!IS_ERR(tsk)) {
@@ -925,8 +926,8 @@ static bool io_wq_work_match_item(struct io_wq_work *work, void *data)
925926
void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work)
926927
{
927928
struct io_wq_acct *acct = io_work_get_acct(wq, work);
929+
unsigned long work_flags = work->flags;
928930
struct io_cb_cancel_data match;
929-
unsigned work_flags = work->flags;
930931
bool do_create;
931932

932933
/*

0 commit comments

Comments
 (0)