Skip to content

Commit 34b3d53

Browse files
pmladektorvalds
authored andcommitted
kthread_worker: split code for canceling the delayed work timer
Patch series "kthread_worker: Fix race between kthread_mod_delayed_work() and kthread_cancel_delayed_work_sync()". This patchset fixes the race between kthread_mod_delayed_work() and kthread_cancel_delayed_work_sync() including proper return value handling. This patch (of 2): Simple code refactoring as a preparation step for fixing a race between kthread_mod_delayed_work() and kthread_cancel_delayed_work_sync(). It does not modify the existing behavior. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Petr Mladek <[email protected]> Cc: <[email protected]> Cc: Martin Liu <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Nathan Chancellor <[email protected]> Cc: Nick Desaulniers <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Tejun Heo <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7ca3027 commit 34b3d53

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed

kernel/kthread.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,33 @@ void kthread_flush_work(struct kthread_work *work)
10921092
}
10931093
EXPORT_SYMBOL_GPL(kthread_flush_work);
10941094

1095+
/*
1096+
* Make sure that the timer is neither set nor running and could
1097+
* not manipulate the work list_head any longer.
1098+
*
1099+
* The function is called under worker->lock. The lock is temporary
1100+
* released but the timer can't be set again in the meantime.
1101+
*/
1102+
static void kthread_cancel_delayed_work_timer(struct kthread_work *work,
1103+
unsigned long *flags)
1104+
{
1105+
struct kthread_delayed_work *dwork =
1106+
container_of(work, struct kthread_delayed_work, work);
1107+
struct kthread_worker *worker = work->worker;
1108+
1109+
/*
1110+
* del_timer_sync() must be called to make sure that the timer
1111+
* callback is not running. The lock must be temporary released
1112+
* to avoid a deadlock with the callback. In the meantime,
1113+
* any queuing is blocked by setting the canceling counter.
1114+
*/
1115+
work->canceling++;
1116+
raw_spin_unlock_irqrestore(&worker->lock, *flags);
1117+
del_timer_sync(&dwork->timer);
1118+
raw_spin_lock_irqsave(&worker->lock, *flags);
1119+
work->canceling--;
1120+
}
1121+
10951122
/*
10961123
* This function removes the work from the worker queue. Also it makes sure
10971124
* that it won't get queued later via the delayed work's timer.
@@ -1106,23 +1133,8 @@ static bool __kthread_cancel_work(struct kthread_work *work, bool is_dwork,
11061133
unsigned long *flags)
11071134
{
11081135
/* Try to cancel the timer if exists. */
1109-
if (is_dwork) {
1110-
struct kthread_delayed_work *dwork =
1111-
container_of(work, struct kthread_delayed_work, work);
1112-
struct kthread_worker *worker = work->worker;
1113-
1114-
/*
1115-
* del_timer_sync() must be called to make sure that the timer
1116-
* callback is not running. The lock must be temporary released
1117-
* to avoid a deadlock with the callback. In the meantime,
1118-
* any queuing is blocked by setting the canceling counter.
1119-
*/
1120-
work->canceling++;
1121-
raw_spin_unlock_irqrestore(&worker->lock, *flags);
1122-
del_timer_sync(&dwork->timer);
1123-
raw_spin_lock_irqsave(&worker->lock, *flags);
1124-
work->canceling--;
1125-
}
1136+
if (is_dwork)
1137+
kthread_cancel_delayed_work_timer(work, flags);
11261138

11271139
/*
11281140
* Try to remove the work from a worker list. It might either

0 commit comments

Comments
 (0)