|
Oliver Neukum |
dfbb70 |
From f571faf6e443b6011ccb585d57866177af1f643c Mon Sep 17 00:00:00 2001
|
|
Oliver Neukum |
dfbb70 |
From: Thomas Gleixner <tglx@linutronix.de>
|
|
Oliver Neukum |
dfbb70 |
Date: Wed, 23 Nov 2022 21:18:53 +0100
|
|
Oliver Neukum |
dfbb70 |
Subject: [PATCH] timers: Provide timer_shutdown[_sync]()
|
|
Oliver Neukum |
dfbb70 |
Git-commit: f571faf6e443b6011ccb585d57866177af1f643c
|
|
Oliver Neukum |
dfbb70 |
References: bsc#1207210
|
|
Oliver Neukum |
dfbb70 |
Patch-mainline: v6.2-rc1
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
Tearing down timers which have circular dependencies to other
|
|
Oliver Neukum |
dfbb70 |
functionality, e.g. workqueues, where the timer can schedule work and work
|
|
Oliver Neukum |
dfbb70 |
can arm timers, is not trivial.
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
In those cases it is desired to shutdown the timer in a way which prevents
|
|
Oliver Neukum |
dfbb70 |
rearming of the timer. The mechanism to do so is to set timer->function to
|
|
Oliver Neukum |
dfbb70 |
NULL and use this as an indicator for the timer arming functions to ignore
|
|
Oliver Neukum |
dfbb70 |
the (re)arm request.
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
Expose new interfaces for this: timer_shutdown_sync() and timer_shutdown().
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
timer_shutdown_sync() has the same functionality as timer_delete_sync()
|
|
Oliver Neukum |
dfbb70 |
plus the NULL-ification of the timer function.
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
timer_shutdown() has the same functionality as timer_delete() plus the
|
|
Oliver Neukum |
dfbb70 |
NULL-ification of the timer function.
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
In both cases the rearming of the timer is prevented by silently discarding
|
|
Oliver Neukum |
dfbb70 |
rearm attempts due to timer->function being NULL.
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
Co-developed-by: Steven Rostedt <rostedt@goodmis.org>
|
|
Oliver Neukum |
dfbb70 |
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
|
|
Oliver Neukum |
dfbb70 |
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
Oliver Neukum |
dfbb70 |
Tested-by: Guenter Roeck <linux@roeck-us.net>
|
|
Oliver Neukum |
dfbb70 |
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
|
Oliver Neukum |
dfbb70 |
Reviewed-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
|
|
Oliver Neukum |
dfbb70 |
Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home
|
|
Oliver Neukum |
dfbb70 |
Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org
|
|
Oliver Neukum |
dfbb70 |
Link: https://lore.kernel.org/r/20221123201625.314230270@linutronix.de
|
|
Oliver Neukum |
dfbb70 |
Signed-off-by: Oliver Neukum <oneukum@suse.com>
|
|
Oliver Neukum |
dfbb70 |
---
|
|
Oliver Neukum |
dfbb70 |
include/linux/timer.h | 2 ++
|
|
Oliver Neukum |
dfbb70 |
kernel/time/timer.c | 66 +++++++++++++++++++++++++++++++++++++++++++
|
|
Oliver Neukum |
dfbb70 |
2 files changed, 68 insertions(+)
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
diff --git a/include/linux/timer.h b/include/linux/timer.h
|
|
Oliver Neukum |
dfbb70 |
index e338e173ce8b..9162f275819a 100644
|
|
Oliver Neukum |
dfbb70 |
--- a/include/linux/timer.h
|
|
Oliver Neukum |
dfbb70 |
+++ b/include/linux/timer.h
|
|
Oliver Neukum |
dfbb70 |
@@ -184,6 +184,8 @@ extern void add_timer(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
extern int try_to_del_timer_sync(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
extern int timer_delete_sync(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
extern int timer_delete(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
+extern int timer_shutdown_sync(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
+extern int timer_shutdown(struct timer_list *timer);
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
/**
|
|
Oliver Neukum |
dfbb70 |
* del_timer_sync - Delete a pending timer and wait for a running callback
|
|
Oliver Neukum |
dfbb70 |
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
|
|
Oliver Neukum |
dfbb70 |
index 167e43c9451c..63a8ce7177dd 100644
|
|
Oliver Neukum |
dfbb70 |
--- a/kernel/time/timer.c
|
|
Oliver Neukum |
dfbb70 |
+++ b/kernel/time/timer.c
|
|
Oliver Neukum |
dfbb70 |
@@ -1362,6 +1362,27 @@ int timer_delete(struct timer_list *timer)
|
|
Oliver Neukum |
dfbb70 |
}
|
|
Oliver Neukum |
dfbb70 |
EXPORT_SYMBOL(timer_delete);
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
+/**
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown - Deactivate a timer and prevent rearming
|
|
Oliver Neukum |
dfbb70 |
+ * @timer: The timer to be deactivated
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * The function does not wait for an eventually running timer callback on a
|
|
Oliver Neukum |
dfbb70 |
+ * different CPU but it prevents rearming of the timer. Any attempt to arm
|
|
Oliver Neukum |
dfbb70 |
+ * @timer after this function returns will be silently ignored.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * This function is useful for teardown code and should only be used when
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown_sync() cannot be invoked due to locking or context constraints.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * Return:
|
|
Oliver Neukum |
dfbb70 |
+ * * %0 - The timer was not pending
|
|
Oliver Neukum |
dfbb70 |
+ * * %1 - The timer was pending
|
|
Oliver Neukum |
dfbb70 |
+ */
|
|
Oliver Neukum |
dfbb70 |
+int timer_shutdown(struct timer_list *timer)
|
|
Oliver Neukum |
dfbb70 |
+{
|
|
Oliver Neukum |
dfbb70 |
+ return __timer_delete(timer, true);
|
|
Oliver Neukum |
dfbb70 |
+}
|
|
Oliver Neukum |
dfbb70 |
+EXPORT_SYMBOL_GPL(timer_shutdown);
|
|
Oliver Neukum |
dfbb70 |
+
|
|
Oliver Neukum |
dfbb70 |
/**
|
|
Oliver Neukum |
dfbb70 |
* __try_to_del_timer_sync - Internal function: Try to deactivate a timer
|
|
Oliver Neukum |
dfbb70 |
* @timer: Timer to deactivate
|
|
Oliver Neukum |
dfbb70 |
@@ -1595,6 +1616,9 @@ static int __timer_delete_sync(struct timer_list *timer, bool shutdown)
|
|
Oliver Neukum |
dfbb70 |
* lock. If there is the possibility of a concurrent rearm then the return
|
|
Oliver Neukum |
dfbb70 |
* value of the function is meaningless.
|
|
Oliver Neukum |
dfbb70 |
*
|
|
Oliver Neukum |
dfbb70 |
+ * If such a guarantee is needed, e.g. for teardown situations then use
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown_sync() instead.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
* Return:
|
|
Oliver Neukum |
dfbb70 |
* * %0 - The timer was not pending
|
|
Oliver Neukum |
dfbb70 |
* * %1 - The timer was pending and deactivated
|
|
Oliver Neukum |
dfbb70 |
@@ -1605,6 +1629,48 @@ int timer_delete_sync(struct timer_list *timer)
|
|
Oliver Neukum |
dfbb70 |
}
|
|
Oliver Neukum |
dfbb70 |
EXPORT_SYMBOL(timer_delete_sync);
|
|
Oliver Neukum |
dfbb70 |
|
|
Oliver Neukum |
dfbb70 |
+/**
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown_sync - Shutdown a timer and prevent rearming
|
|
Oliver Neukum |
dfbb70 |
+ * @timer: The timer to be shutdown
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * When the function returns it is guaranteed that:
|
|
Oliver Neukum |
dfbb70 |
+ * - @timer is not queued
|
|
Oliver Neukum |
dfbb70 |
+ * - The callback function of @timer is not running
|
|
Oliver Neukum |
dfbb70 |
+ * - @timer cannot be enqueued again. Any attempt to rearm
|
|
Oliver Neukum |
dfbb70 |
+ * @timer is silently ignored.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * See timer_delete_sync() for synchronization rules.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * This function is useful for final teardown of an infrastructure where
|
|
Oliver Neukum |
dfbb70 |
+ * the timer is subject to a circular dependency problem.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * A common pattern for this is a timer and a workqueue where the timer can
|
|
Oliver Neukum |
dfbb70 |
+ * schedule work and work can arm the timer. On shutdown the workqueue must
|
|
Oliver Neukum |
dfbb70 |
+ * be destroyed and the timer must be prevented from rearming. Unless the
|
|
Oliver Neukum |
dfbb70 |
+ * code has conditionals like 'if (mything->in_shutdown)' to prevent that
|
|
Oliver Neukum |
dfbb70 |
+ * there is no way to get this correct with timer_delete_sync().
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown_sync() is solving the problem. The correct ordering of
|
|
Oliver Neukum |
dfbb70 |
+ * calls in this case is:
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * timer_shutdown_sync(&mything->timer);
|
|
Oliver Neukum |
dfbb70 |
+ * workqueue_destroy(&mything->workqueue);
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * After this 'mything' can be safely freed.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * This obviously implies that the timer is not required to be functional
|
|
Oliver Neukum |
dfbb70 |
+ * for the rest of the shutdown operation.
|
|
Oliver Neukum |
dfbb70 |
+ *
|
|
Oliver Neukum |
dfbb70 |
+ * Return:
|
|
Oliver Neukum |
dfbb70 |
+ * * %0 - The timer was not pending
|
|
Oliver Neukum |
dfbb70 |
+ * * %1 - The timer was pending
|
|
Oliver Neukum |
dfbb70 |
+ */
|
|
Oliver Neukum |
dfbb70 |
+int timer_shutdown_sync(struct timer_list *timer)
|
|
Oliver Neukum |
dfbb70 |
+{
|
|
Oliver Neukum |
dfbb70 |
+ return __timer_delete_sync(timer, true);
|
|
Oliver Neukum |
dfbb70 |
+}
|
|
Oliver Neukum |
dfbb70 |
+EXPORT_SYMBOL_GPL(timer_shutdown_sync);
|
|
Oliver Neukum |
dfbb70 |
+
|
|
Oliver Neukum |
dfbb70 |
static void call_timer_fn(struct timer_list *timer,
|
|
Oliver Neukum |
dfbb70 |
void (*fn)(struct timer_list *),
|
|
Oliver Neukum |
dfbb70 |
unsigned long baseclk)
|
|
Oliver Neukum |
dfbb70 |
--
|
|
Oliver Neukum |
dfbb70 |
2.39.0
|
|
Oliver Neukum |
dfbb70 |
|