[PATCH] kthread_worker: Flush all delayed works when destroy kthread worker

From: Zqiang
Date: Fri Dec 23 2022 - 08:10:36 EST


When destroy a kthread worker, only flush all current works on
kthread worker, this is not very sufficient, there may be some
delayed works in the pending state, this commit therefore add
flush delayed works function in kthread_destroy_worker().

Signed-off-by: Zqiang <qiang1.zhang@xxxxxxxxx>
---
include/linux/kthread.h | 1 +
kernel/kthread.c | 31 +++++++++++++++++++++++++++++++
2 files changed, 32 insertions(+)

diff --git a/include/linux/kthread.h b/include/linux/kthread.h
index 30e5bec81d2b..8616228abb3b 100644
--- a/include/linux/kthread.h
+++ b/include/linux/kthread.h
@@ -206,6 +206,7 @@ bool kthread_mod_delayed_work(struct kthread_worker *worker,

void kthread_flush_work(struct kthread_work *work);
void kthread_flush_worker(struct kthread_worker *worker);
+void kthread_flush_delayed_works(struct kthread_worker *worker);

bool kthread_cancel_work_sync(struct kthread_work *work);
bool kthread_cancel_delayed_work_sync(struct kthread_delayed_work *work);
diff --git a/kernel/kthread.c b/kernel/kthread.c
index f97fd01a2932..2744f6b769d1 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -1375,6 +1375,35 @@ void kthread_flush_worker(struct kthread_worker *worker)
}
EXPORT_SYMBOL_GPL(kthread_flush_worker);

+/**
+ * kthread_flush_delayed_works - flush all current delayed works on a
+ * kthread_worker.
+ * @worker: worker to flush
+ *
+ * Wait until all currently executing or pending delayed works are
+ * queued completed.
+ */
+void kthread_flush_delayed_works(struct kthread_worker *worker)
+{
+ unsigned long flags;
+ struct kthread_delayed_work *dwork;
+ struct kthread_work *work;
+
+ raw_spin_lock_irqsave(&worker->lock, flags);
+ while (!list_empty(&worker->delayed_work_list)) {
+ work = list_first_entry(&worker->delayed_work_list,
+ struct kthread_work, node);
+ list_del_init(&work->node);
+ dwork = container_of(work, struct kthread_delayed_work, work);
+ raw_spin_unlock_irqrestore(&worker->lock, flags);
+ if (del_timer_sync(&dwork->timer))
+ kthread_queue_work(worker, &dwork->work);
+ raw_spin_lock_irqsave(&worker->lock, flags);
+ }
+ raw_spin_unlock_irqrestore(&worker->lock, flags);
+}
+EXPORT_SYMBOL_GPL(kthread_flush_delayed_works);
+
/**
* kthread_destroy_worker - destroy a kthread worker
* @worker: worker to be destroyed
@@ -1391,8 +1420,10 @@ void kthread_destroy_worker(struct kthread_worker *worker)
if (WARN_ON(!task))
return;

+ kthread_flush_delayed_works(worker);
kthread_flush_worker(worker);
kthread_stop(task);
+ WARN_ON(!list_empty(&worker->delayed_work_list));
WARN_ON(!list_empty(&worker->work_list));
kfree(worker);
}
--
2.25.1