Skip to content

Commit de36a15

Browse files
committed
io_uring/io-wq: reduce frequency of acct->lock acquisitions
When we check if we have work to run, we grab the acct lock, check, drop it, and then return the result. If we do have work to run, then running the work will again grab acct->lock and get the work item. This causes us to grab acct->lock more frequently than we need to. If we have work to do, have io_acct_run_queue() return with the acct lock still acquired. io_worker_handle_work() is then always invoked with the acct lock already held. In a simple test cases that stats files (IORING_OP_STATX always hits io-wq), we see a nice reduction in locking overhead with this change: 19.32% -12.55% [kernel.kallsyms] [k] __cmpwait_case_32 20.90% -12.07% [kernel.kallsyms] [k] queued_spin_lock_slowpath Reviewed-by: Hao Xu <howeyxu@tencent.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 78848b9 commit de36a15

1 file changed

Lines changed: 34 additions & 13 deletions

File tree

io_uring/io-wq.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,17 +232,25 @@ static void io_worker_exit(struct io_worker *worker)
232232
do_exit(0);
233233
}
234234

235-
static inline bool io_acct_run_queue(struct io_wq_acct *acct)
235+
static inline bool __io_acct_run_queue(struct io_wq_acct *acct)
236236
{
237-
bool ret = false;
237+
return !test_bit(IO_ACCT_STALLED_BIT, &acct->flags) &&
238+
!wq_list_empty(&acct->work_list);
239+
}
238240

241+
/*
242+
* If there's work to do, returns true with acct->lock acquired. If not,
243+
* returns false with no lock held.
244+
*/
245+
static inline bool io_acct_run_queue(struct io_wq_acct *acct)
246+
__acquires(&acct->lock)
247+
{
239248
raw_spin_lock(&acct->lock);
240-
if (!wq_list_empty(&acct->work_list) &&
241-
!test_bit(IO_ACCT_STALLED_BIT, &acct->flags))
242-
ret = true;
243-
raw_spin_unlock(&acct->lock);
249+
if (__io_acct_run_queue(acct))
250+
return true;
244251

245-
return ret;
252+
raw_spin_unlock(&acct->lock);
253+
return false;
246254
}
247255

248256
/*
@@ -397,6 +405,7 @@ static void io_wq_dec_running(struct io_worker *worker)
397405
if (!io_acct_run_queue(acct))
398406
return;
399407

408+
raw_spin_unlock(&acct->lock);
400409
atomic_inc(&acct->nr_running);
401410
atomic_inc(&wq->worker_refs);
402411
io_queue_worker_create(worker, acct, create_worker_cb);
@@ -521,9 +530,13 @@ static void io_assign_current_work(struct io_worker *worker,
521530
raw_spin_unlock(&worker->lock);
522531
}
523532

524-
static void io_worker_handle_work(struct io_worker *worker)
533+
/*
534+
* Called with acct->lock held, drops it before returning
535+
*/
536+
static void io_worker_handle_work(struct io_wq_acct *acct,
537+
struct io_worker *worker)
538+
__releases(&acct->lock)
525539
{
526-
struct io_wq_acct *acct = io_wq_get_acct(worker);
527540
struct io_wq *wq = worker->wq;
528541
bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
529542

@@ -537,7 +550,6 @@ static void io_worker_handle_work(struct io_worker *worker)
537550
* can't make progress, any work completion or insertion will
538551
* clear the stalled flag.
539552
*/
540-
raw_spin_lock(&acct->lock);
541553
work = io_get_next_work(acct, worker);
542554
raw_spin_unlock(&acct->lock);
543555
if (work) {
@@ -591,6 +603,10 @@ static void io_worker_handle_work(struct io_worker *worker)
591603
wake_up(&wq->hash->wait);
592604
}
593605
} while (work);
606+
607+
if (!__io_acct_run_queue(acct))
608+
break;
609+
raw_spin_lock(&acct->lock);
594610
} while (1);
595611
}
596612

@@ -611,8 +627,13 @@ static int io_wq_worker(void *data)
611627
long ret;
612628

613629
set_current_state(TASK_INTERRUPTIBLE);
630+
631+
/*
632+
* If we have work to do, io_acct_run_queue() returns with
633+
* the acct->lock held. If not, it will drop it.
634+
*/
614635
while (io_acct_run_queue(acct))
615-
io_worker_handle_work(worker);
636+
io_worker_handle_work(acct, worker);
616637

617638
raw_spin_lock(&wq->lock);
618639
/*
@@ -645,8 +666,8 @@ static int io_wq_worker(void *data)
645666
}
646667
}
647668

648-
if (test_bit(IO_WQ_BIT_EXIT, &wq->state))
649-
io_worker_handle_work(worker);
669+
if (test_bit(IO_WQ_BIT_EXIT, &wq->state) && io_acct_run_queue(acct))
670+
io_worker_handle_work(acct, worker);
650671

651672
io_worker_exit(worker);
652673
return 0;

0 commit comments

Comments
 (0)