|
Petr Mladek |
5cb179 |
From 34b3d5344719d14fd2185b2d9459b3abcb8cf9d8 Mon Sep 17 00:00:00 2001
|
|
Petr Mladek |
5cb179 |
From: Petr Mladek <pmladek@suse.com>
|
|
Petr Mladek |
5cb179 |
Date: Thu, 24 Jun 2021 18:39:45 -0700
|
|
Petr Mladek |
5cb179 |
Subject: [PATCH] kthread_worker: split code for canceling the delayed work
|
|
Petr Mladek |
5cb179 |
timer
|
|
Petr Mladek |
5cb179 |
Git-commit: 34b3d5344719d14fd2185b2d9459b3abcb8cf9d8
|
|
Petr Mladek |
5cb179 |
Patch-mainline: v5.13
|
|
Petr Mladek |
5cb179 |
References: bsc#1187867
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
Patch series "kthread_worker: Fix race between kthread_mod_delayed_work()
|
|
Petr Mladek |
5cb179 |
and kthread_cancel_delayed_work_sync()".
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
This patchset fixes the race between kthread_mod_delayed_work() and
|
|
Petr Mladek |
5cb179 |
kthread_cancel_delayed_work_sync() including proper return value
|
|
Petr Mladek |
5cb179 |
handling.
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
This patch (of 2):
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
Simple code refactoring as a preparation step for fixing a race between
|
|
Petr Mladek |
5cb179 |
kthread_mod_delayed_work() and kthread_cancel_delayed_work_sync().
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
It does not modify the existing behavior.
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
Link: https://lkml.kernel.org/r/20210610133051.15337-2-pmladek@suse.com
|
|
Petr Mladek |
5cb179 |
Signed-off-by: Petr Mladek <pmladek@suse.com>
|
|
Petr Mladek |
5cb179 |
Cc: <jenhaochen@google.com>
|
|
Petr Mladek |
5cb179 |
Cc: Martin Liu <liumartin@google.com>
|
|
Petr Mladek |
5cb179 |
Cc: Minchan Kim <minchan@google.com>
|
|
Petr Mladek |
5cb179 |
Cc: Nathan Chancellor <nathan@kernel.org>
|
|
Petr Mladek |
5cb179 |
Cc: Nick Desaulniers <ndesaulniers@google.com>
|
|
Petr Mladek |
5cb179 |
Cc: Oleg Nesterov <oleg@redhat.com>
|
|
Petr Mladek |
5cb179 |
Cc: Tejun Heo <tj@kernel.org>
|
|
Petr Mladek |
5cb179 |
Cc: <stable@vger.kernel.org>
|
|
Petr Mladek |
5cb179 |
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
Petr Mladek |
5cb179 |
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
---
|
|
Petr Mladek |
5cb179 |
kernel/kthread.c | 46 +++++++++++++++++++++++++++++-----------------
|
|
Petr Mladek |
5cb179 |
1 file changed, 29 insertions(+), 17 deletions(-)
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
diff --git a/kernel/kthread.c b/kernel/kthread.c
|
|
Petr Mladek |
5cb179 |
index fe3f2a40d61e..121a0e1fc659 100644
|
|
Petr Mladek |
5cb179 |
--- a/kernel/kthread.c
|
|
Petr Mladek |
5cb179 |
+++ b/kernel/kthread.c
|
|
Petr Mladek |
5cb179 |
@@ -1092,6 +1092,33 @@ void kthread_flush_work(struct kthread_work *work)
|
|
Petr Mladek |
5cb179 |
}
|
|
Petr Mladek |
5cb179 |
EXPORT_SYMBOL_GPL(kthread_flush_work);
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
+/*
|
|
Petr Mladek |
5cb179 |
+ * Make sure that the timer is neither set nor running and could
|
|
Petr Mladek |
5cb179 |
+ * not manipulate the work list_head any longer.
|
|
Petr Mladek |
5cb179 |
+ *
|
|
Petr Mladek |
5cb179 |
+ * The function is called under worker->lock. The lock is temporary
|
|
Petr Mladek |
5cb179 |
+ * released but the timer can't be set again in the meantime.
|
|
Petr Mladek |
5cb179 |
+ */
|
|
Petr Mladek |
5cb179 |
+static void kthread_cancel_delayed_work_timer(struct kthread_work *work,
|
|
Petr Mladek |
5cb179 |
+ unsigned long *flags)
|
|
Petr Mladek |
5cb179 |
+{
|
|
Petr Mladek |
5cb179 |
+ struct kthread_delayed_work *dwork =
|
|
Petr Mladek |
5cb179 |
+ container_of(work, struct kthread_delayed_work, work);
|
|
Petr Mladek |
5cb179 |
+ struct kthread_worker *worker = work->worker;
|
|
Petr Mladek |
5cb179 |
+
|
|
Petr Mladek |
5cb179 |
+ /*
|
|
Petr Mladek |
5cb179 |
+ * del_timer_sync() must be called to make sure that the timer
|
|
Petr Mladek |
5cb179 |
+ * callback is not running. The lock must be temporary released
|
|
Petr Mladek |
5cb179 |
+ * to avoid a deadlock with the callback. In the meantime,
|
|
Petr Mladek |
5cb179 |
+ * any queuing is blocked by setting the canceling counter.
|
|
Petr Mladek |
5cb179 |
+ */
|
|
Petr Mladek |
5cb179 |
+ work->canceling++;
|
|
Petr Mladek |
5cb179 |
+ spin_unlock_irqrestore(&worker->lock, *flags);
|
|
Petr Mladek |
5cb179 |
+ del_timer_sync(&dwork->timer);
|
|
Petr Mladek |
5cb179 |
+ spin_lock_irqsave(&worker->lock, *flags);
|
|
Petr Mladek |
5cb179 |
+ work->canceling--;
|
|
Petr Mladek |
5cb179 |
+}
|
|
Petr Mladek |
5cb179 |
+
|
|
Petr Mladek |
5cb179 |
/*
|
|
Petr Mladek |
5cb179 |
* This function removes the work from the worker queue. Also it makes sure
|
|
Petr Mladek |
5cb179 |
* that it won't get queued later via the delayed work's timer.
|
|
Petr Mladek |
5cb179 |
@@ -1106,23 +1133,8 @@ static bool __kthread_cancel_work(struct kthread_work *work, bool is_dwork,
|
|
Petr Mladek |
5cb179 |
unsigned long *flags)
|
|
Petr Mladek |
5cb179 |
{
|
|
Petr Mladek |
5cb179 |
/* Try to cancel the timer if exists. */
|
|
Petr Mladek |
5cb179 |
- if (is_dwork) {
|
|
Petr Mladek |
5cb179 |
- struct kthread_delayed_work *dwork =
|
|
Petr Mladek |
5cb179 |
- container_of(work, struct kthread_delayed_work, work);
|
|
Petr Mladek |
5cb179 |
- struct kthread_worker *worker = work->worker;
|
|
Petr Mladek |
5cb179 |
-
|
|
Petr Mladek |
5cb179 |
- /*
|
|
Petr Mladek |
5cb179 |
- * del_timer_sync() must be called to make sure that the timer
|
|
Petr Mladek |
5cb179 |
- * callback is not running. The lock must be temporary released
|
|
Petr Mladek |
5cb179 |
- * to avoid a deadlock with the callback. In the meantime,
|
|
Petr Mladek |
5cb179 |
- * any queuing is blocked by setting the canceling counter.
|
|
Petr Mladek |
5cb179 |
- */
|
|
Petr Mladek |
5cb179 |
- work->canceling++;
|
|
Petr Mladek |
5cb179 |
- spin_unlock_irqrestore(&worker->lock, *flags);
|
|
Petr Mladek |
5cb179 |
- del_timer_sync(&dwork->timer);
|
|
Petr Mladek |
5cb179 |
- spin_lock_irqsave(&worker->lock, *flags);
|
|
Petr Mladek |
5cb179 |
- work->canceling--;
|
|
Petr Mladek |
5cb179 |
- }
|
|
Petr Mladek |
5cb179 |
+ if (is_dwork)
|
|
Petr Mladek |
5cb179 |
+ kthread_cancel_delayed_work_timer(work, flags);
|
|
Petr Mladek |
5cb179 |
|
|
Petr Mladek |
5cb179 |
/*
|
|
Petr Mladek |
5cb179 |
* Try to remove the work from a worker list. It might either
|
|
Petr Mladek |
5cb179 |
--
|
|
Petr Mladek |
5cb179 |
2.26.2
|
|
Petr Mladek |
5cb179 |
|