timer_shutdown_sync() function is useful for final teardown of an infrastructure where the timer is subject to a circular dependency problem.
A common pattern for this is a timer and a workqueue where the timer can schedule work and work can arm the timer. On shutdown the workqueue must be destroyed and the timer must be prevented from rearming. Unless the code has conditionals like 'if (mything->in_shutdown)' to prevent that there is no way to get this correct with timer_delete_sync().
timer_shutdown_sync() is solving the problem. The correct ordering of calls in this case is:
timer_shutdown_sync(&mything->timer); workqueue_destroy(&mything->workqueue);
After this 'mything' can be safely freed.
Steven Rostedt (Google) (4): ARM: spear: Do not use timer namespace for timer_shutdown() function clocksource/drivers/arm_arch_timer: Do not use timer namespace for timer_shutdown() function clocksource/drivers/sp804: Do not use timer namespace for timer_shutdown() function timers: Update the documentation to reflect on the new timer_shutdown() API
Thomas Gleixner (12): Documentation: Remove bogus claim about del_timer_sync() timers: Get rid of del_singleshot_timer_sync() timers: Replace BUG_ON()s timers: Update kernel-doc for various functions timers: Use del_timer_sync() even on UP timers: Rename del_timer_sync() to timer_delete_sync() timers: Rename del_timer() to timer_delete() Documentation: Replace del_timer/del_timer_sync() timers: Silently ignore timers with a NULL function timers: Split [try_to_]del_timer[_sync]() to prepare for shutdown mode timers: Add shutdown mechanism to the internal functions timers: Provide timer_shutdown[_sync]()
Yu Liao (1): timers: Keep del_timer[_sync]() exported
.../RCU/Design/Requirements/Requirements.rst | 2 +- Documentation/core-api/local_ops.rst | 2 +- Documentation/kernel-hacking/locking.rst | 17 +- Documentation/timers/hrtimers.rst | 2 +- .../it_IT/kernel-hacking/locking.rst | 14 +- .../translations/zh_CN/core-api/local_ops.rst | 196 ++++++++ arch/arm/mach-spear/time.c | 8 +- drivers/char/tpm/tpm-dev-common.c | 4 +- drivers/clocksource/arm_arch_timer.c | 12 +- drivers/clocksource/timer-sp804.c | 6 +- drivers/misc/sgi-xp/xpc_partition.c | 2 +- drivers/staging/wlan-ng/hfa384x_usb.c | 4 +- drivers/staging/wlan-ng/prism2usb.c | 6 +- include/linux/timer.h | 15 +- kernel/time/timer.c | 449 ++++++++++++++---- net/sunrpc/xprt.c | 2 +- 16 files changed, 597 insertions(+), 144 deletions(-) create mode 100644 Documentation/translations/zh_CN/core-api/local_ops.rst
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit b0b0aa5d858d4d2fe39a5e4486e0550e858108f6 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
del_timer_sync() does not return the number of times it tried to delete the timer which rearms itself. It's clearly documented:
The function returns whether it has deactivated a pending timer or not.
This part of the documentation is from 2003 where del_timer_sync() really returned the number of deletion attempts for unknown reasons. The code was rewritten in 2005, but the documentation was not updated.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201624.452282769@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- Documentation/kernel-hacking/locking.rst | 3 +-- Documentation/translations/it_IT/kernel-hacking/locking.rst | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/Documentation/kernel-hacking/locking.rst b/Documentation/kernel-hacking/locking.rst index 6ed806e6061b..d5ad37467d0c 100644 --- a/Documentation/kernel-hacking/locking.rst +++ b/Documentation/kernel-hacking/locking.rst @@ -1015,8 +1015,7 @@ Another common problem is deleting timers which restart themselves (by calling add_timer() at the end of their timer function). Because this is a fairly common case which is prone to races, you should use del_timer_sync() (``include/linux/timer.h``) to -handle this case. It returns the number of times the timer had to be -deleted before we finally stopped it from adding itself back in. +handle this case.
Locking Speed ============= diff --git a/Documentation/translations/it_IT/kernel-hacking/locking.rst b/Documentation/translations/it_IT/kernel-hacking/locking.rst index bf1acd6204ef..587ae37d2ee9 100644 --- a/Documentation/translations/it_IT/kernel-hacking/locking.rst +++ b/Documentation/translations/it_IT/kernel-hacking/locking.rst @@ -1037,9 +1037,7 @@ Un altro problema �� l'eliminazione dei temporizzatori che si riavviano da soli (chiamando add_timer() alla fine della loro esecuzione). Dato che questo �� un problema abbastanza comune con una propensione alle corse critiche, dovreste usare del_timer_sync() -(``include/linux/timer.h``) per gestire questo caso. Questa ritorna il -numero di volte che il temporizzatore �� stato interrotto prima che -fosse in grado di fermarlo senza che si riavviasse. +(``include/linux/timer.h``) per gestire questo caso.
Velocit�� della sincronizzazione ===============================
From: "Steven Rostedt (Google)" rostedt@goodmis.org
mainline inclusion from mainline-v6.2-rc1 commit 80b55772d41d8afec68dbc4ff0368a9fe5d1f390 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
A new "shutdown" timer state is being added to the generic timer code. One of the functions to change the timer into the state is called "timer_shutdown()". This means that there can not be other functions called "timer_shutdown()" as the timer code owns the "timer_*" name space.
Rename timer_shutdown() to spear_timer_shutdown() to avoid this conflict.
Signed-off-by: Steven Rostedt (Google) rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Acked-by: Arnd Bergmann arnd@arndb.de Acked-by: Viresh Kumar viresh.kumar@linaro.org Link: https://lkml.kernel.org/r/20221106212701.822440504@goodmis.org Link: https://lore.kernel.org/all/20221105060155.228348078@goodmis.org/ Link: https://lore.kernel.org/r/20221110064146.810953418@goodmis.org Link: https://lore.kernel.org/r/20221123201624.513863211@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- arch/arm/mach-spear/time.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-spear/time.c b/arch/arm/mach-spear/time.c index d1fdb6066f7b..7b039f359338 100644 --- a/arch/arm/mach-spear/time.c +++ b/arch/arm/mach-spear/time.c @@ -93,7 +93,7 @@ static void __init spear_clocksource_init(void) 200, 16, clocksource_mmio_readw_up); }
-static inline void timer_shutdown(struct clock_event_device *evt) +static inline void spear_timer_shutdown(struct clock_event_device *evt) { u16 val = readw(gpt_base + CR(CLKEVT));
@@ -104,7 +104,7 @@ static inline void timer_shutdown(struct clock_event_device *evt)
static int spear_shutdown(struct clock_event_device *evt) { - timer_shutdown(evt); + spear_timer_shutdown(evt);
return 0; } @@ -114,7 +114,7 @@ static int spear_set_oneshot(struct clock_event_device *evt) u16 val;
/* stop the timer */ - timer_shutdown(evt); + spear_timer_shutdown(evt);
val = readw(gpt_base + CR(CLKEVT)); val |= CTRL_ONE_SHOT; @@ -129,7 +129,7 @@ static int spear_set_periodic(struct clock_event_device *evt) u16 val;
/* stop the timer */ - timer_shutdown(evt); + spear_timer_shutdown(evt);
period = clk_get_rate(gpt_clk) / HZ; period >>= CTRL_PRESCALER16;
From: "Steven Rostedt (Google)" rostedt@goodmis.org
mainline inclusion from mainline-v6.2-rc1 commit 73737a5833ace25a8408b0d3b783637cb6bf29d1 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
A new "shutdown" timer state is being added to the generic timer code. One of the functions to change the timer into the state is called "timer_shutdown()". This means that there can not be other functions called "timer_shutdown()" as the timer code owns the "timer_*" name space.
Rename timer_shutdown() to arch_timer_shutdown() to avoid this conflict.
Signed-off-by: Steven Rostedt (Google) rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Acked-by: Marc Zyngier maz@kernel.org Link: https://lkml.kernel.org/r/20221106212702.002251651@goodmis.org Link: https://lore.kernel.org/all/20221105060155.409832154@goodmis.org/ Link: https://lore.kernel.org/r/20221110064146.981725531@goodmis.org Link: https://lore.kernel.org/r/20221123201624.574672568@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- drivers/clocksource/arm_arch_timer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 432aeb7051fa..96197e8f6b93 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -679,8 +679,8 @@ static irqreturn_t arch_timer_handler_virt_mem(int irq, void *dev_id) return timer_handler(ARCH_TIMER_MEM_VIRT_ACCESS, evt); }
-static __always_inline int timer_shutdown(const int access, - struct clock_event_device *clk) +static __always_inline int arch_timer_shutdown(const int access, + struct clock_event_device *clk) { unsigned long ctrl;
@@ -693,22 +693,22 @@ static __always_inline int timer_shutdown(const int access,
static int arch_timer_shutdown_virt(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_VIRT_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_VIRT_ACCESS, clk); }
static int arch_timer_shutdown_phys(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk); }
static int arch_timer_shutdown_virt_mem(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk); }
static int arch_timer_shutdown_phys_mem(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk); }
static __always_inline void set_next_event(const int access, unsigned long evt,
From: "Steven Rostedt (Google)" rostedt@goodmis.org
mainline inclusion from mainline-v6.2-rc1 commit 6e1fc2591f116dfb20b65cf27356475461d61bd8 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
A new "shutdown" timer state is being added to the generic timer code. One of the functions to change the timer into the state is called "timer_shutdown()". This means that there can not be other functions called "timer_shutdown()" as the timer code owns the "timer_*" name space.
Rename timer_shutdown() to evt_timer_shutdown() to avoid this conflict.
Signed-off-by: Steven Rostedt (Google) rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lkml.kernel.org/r/20221106212702.182883323@goodmis.org Link: https://lore.kernel.org/all/20221105060155.592778858@goodmis.org/ Link: https://lore.kernel.org/r/20221110064147.158230501@goodmis.org Link: https://lore.kernel.org/r/20221123201624.634354813@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- drivers/clocksource/timer-sp804.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c index bedd3570474b..cf3e7c6f86c4 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -170,14 +170,14 @@ static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; }
-static inline void timer_shutdown(struct clock_event_device *evt) +static inline void evt_timer_shutdown(struct clock_event_device *evt) { writel(0, common_clkevt->ctrl); }
static int sp804_shutdown(struct clock_event_device *evt) { - timer_shutdown(evt); + evt_timer_shutdown(evt); return 0; }
@@ -186,7 +186,7 @@ static int sp804_set_periodic(struct clock_event_device *evt) unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
- timer_shutdown(evt); + evt_timer_shutdown(evt); writel(common_clkevt->reload, common_clkevt->load); writel(ctrl, common_clkevt->ctrl); return 0;
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 9a5a305686971f4be10c6d7251c8348d74b3e014 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
del_singleshot_timer_sync() used to be an optimization for deleting timers which are not rearmed from the timer callback function.
This optimization turned out to be broken and got mapped to del_timer_sync() about 17 years ago.
Get rid of the undocumented indirection and use del_timer_sync() directly.
No functional change.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201624.706987932@linutronix.de
Conflicts: net/sunrpc/xprt.c Signed-off-by: Yu Liao liaoyu15@huawei.com --- drivers/char/tpm/tpm-dev-common.c | 4 ++-- drivers/misc/sgi-xp/xpc_partition.c | 2 +- drivers/staging/wlan-ng/hfa384x_usb.c | 4 ++-- drivers/staging/wlan-ng/prism2usb.c | 6 +++--- include/linux/timer.h | 2 -- kernel/time/timer.c | 2 +- net/sunrpc/xprt.c | 2 +- 7 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index b99e1941c52c..a9e7f5a82262 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -156,7 +156,7 @@ ssize_t tpm_common_read(struct file *file, char __user *buf, out: if (!priv->response_length) { *off = 0; - del_singleshot_timer_sync(&priv->user_read_timer); + del_timer_sync(&priv->user_read_timer); flush_work(&priv->timeout_work); } mutex_unlock(&priv->buffer_mutex); @@ -263,7 +263,7 @@ __poll_t tpm_common_poll(struct file *file, poll_table *wait) void tpm_common_release(struct file *file, struct file_priv *priv) { flush_work(&priv->async_work); - del_singleshot_timer_sync(&priv->user_read_timer); + del_timer_sync(&priv->user_read_timer); flush_work(&priv->timeout_work); file->private_data = NULL; priv->response_length = 0; diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 57df06820bae..c56e10168d40 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -291,7 +291,7 @@ xpc_partition_disengaged(struct xpc_partition *part)
/* cancel the timer function, provided it's not us */ if (!in_interrupt()) - del_singleshot_timer_sync(&part->disengage_timer); + del_timer_sync(&part->disengage_timer);
DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING && part->act_state != XPC_P_AS_INACTIVE); diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c index fac3f34d4a1f..9d4092f84c9b 100644 --- a/drivers/staging/wlan-ng/hfa384x_usb.c +++ b/drivers/staging/wlan-ng/hfa384x_usb.c @@ -1116,8 +1116,8 @@ static int hfa384x_usbctlx_complete_sync(struct hfa384x *hw, if (ctlx == get_active_ctlx(hw)) { spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
- del_singleshot_timer_sync(&hw->reqtimer); - del_singleshot_timer_sync(&hw->resptimer); + del_timer_sync(&hw->reqtimer); + del_timer_sync(&hw->resptimer); hw->req_timer_done = 1; hw->resp_timer_done = 1; usb_kill_urb(&hw->ctlx_urb); diff --git a/drivers/staging/wlan-ng/prism2usb.c b/drivers/staging/wlan-ng/prism2usb.c index 4b08dc1da4f9..83fcb937a58e 100644 --- a/drivers/staging/wlan-ng/prism2usb.c +++ b/drivers/staging/wlan-ng/prism2usb.c @@ -171,9 +171,9 @@ static void prism2sta_disconnect_usb(struct usb_interface *interface) */ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
- del_singleshot_timer_sync(&hw->throttle); - del_singleshot_timer_sync(&hw->reqtimer); - del_singleshot_timer_sync(&hw->resptimer); + del_timer_sync(&hw->throttle); + del_timer_sync(&hw->reqtimer); + del_timer_sync(&hw->resptimer);
/* Unlink all the URBs. This "removes the wheels" * from the entire CTLX handling mechanism. diff --git a/include/linux/timer.h b/include/linux/timer.h index d2dc1cb8b9f8..e7c9108f73a5 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -196,8 +196,6 @@ extern int try_to_del_timer_sync(struct timer_list *timer); # define del_timer_sync(t) del_timer(t) #endif
-#define del_singleshot_timer_sync(t) del_timer_sync(t) - extern void init_timers(void); extern void run_local_timers(void); struct hrtimer; diff --git a/kernel/time/timer.c b/kernel/time/timer.c index f7d3a108e27c..6987a4bb44cc 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1875,7 +1875,7 @@ signed long __sched schedule_timeout(signed long timeout) timer_setup_on_stack(&timer.timer, process_timeout, 0); __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING); schedule(); - del_singleshot_timer_sync(&timer.timer); + del_timer_sync(&timer.timer);
/* Remove the timer from the object tracker */ destroy_timer_on_stack(&timer.timer); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 9328fc1dbf1f..e9f43f80d563 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1143,7 +1143,7 @@ xprt_request_enqueue_receive(struct rpc_task *task) spin_unlock(&xprt->queue_lock);
/* Turn off autodisconnect */ - del_singleshot_timer_sync(&xprt->timer); + del_timer_sync(&xprt->timer); }
/**
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 82ed6f7ef58f9634fe4462dd721902c580f01569 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The timer code still has a few BUG_ON()s left which are crashing the kernel in situations where it still can recover or simply refuse to take an action.
Remove the one in the hotplug callback which checks for the CPU being offline. If that happens then the whole hotplug machinery will explode in colourful ways.
Replace the rest with WARN_ON_ONCE() and conditional returns where appropriate.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201624.769128888@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- kernel/time/timer.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 6987a4bb44cc..6c8c9805a2fc 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1140,7 +1140,8 @@ EXPORT_SYMBOL(timer_reduce); */ void add_timer(struct timer_list *timer) { - BUG_ON(timer_pending(timer)); + if (WARN_ON_ONCE(timer_pending(timer))) + return; __mod_timer(timer, timer->expires, MOD_TIMER_NOTPENDING); } EXPORT_SYMBOL(add_timer); @@ -1157,7 +1158,8 @@ void add_timer_on(struct timer_list *timer, int cpu) struct timer_base *new_base, *base; unsigned long flags;
- BUG_ON(timer_pending(timer) || !timer->function); + if (WARN_ON_ONCE(timer_pending(timer) || !timer->function)) + return;
new_base = get_timer_cpu_base(timer->flags, cpu);
@@ -1958,8 +1960,6 @@ int timers_dead_cpu(unsigned int cpu) struct timer_base *new_base; int b, i;
- BUG_ON(cpu_online(cpu)); - for (b = 0; b < NR_BASES; b++) { old_base = per_cpu_ptr(&timer_bases[b], cpu); new_base = get_cpu_ptr(&timer_bases[b]); @@ -1976,7 +1976,8 @@ int timers_dead_cpu(unsigned int cpu) */ forward_timer_base(new_base);
- BUG_ON(old_base->running_timer); + WARN_ON_ONCE(old_base->running_timer); + old_base->running_timer = NULL;
for (i = 0; i < WHEEL_SIZE; i++) migrate_timer_list(new_base, old_base->vectors + i);
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 14f043f1340bf30bc60af127bff39f55889fef26 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The kernel-doc of timer related functions is partially uncomprehensible word salad. Rewrite it to make it useful.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201624.828703870@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- kernel/time/timer.c | 148 +++++++++++++++++++++++++++----------------- 1 file changed, 90 insertions(+), 58 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 6c8c9805a2fc..cbec76a98ac0 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1068,14 +1068,16 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option }
/** - * mod_timer_pending - modify a pending timer's timeout - * @timer: the pending timer to be modified - * @expires: new timeout in jiffies + * mod_timer_pending - Modify a pending timer's timeout + * @timer: The pending timer to be modified + * @expires: New absolute timeout in jiffies * - * mod_timer_pending() is the same for pending timers as mod_timer(), - * but will not re-activate and modify already deleted timers. + * mod_timer_pending() is the same for pending timers as mod_timer(), but + * will not activate inactive timers. * - * It is useful for unserialized use of timers. + * Return: + * * %0 - The timer was inactive and not modified + * * %1 - The timer was active and requeued to expire at @expires */ int mod_timer_pending(struct timer_list *timer, unsigned long expires) { @@ -1084,24 +1086,27 @@ int mod_timer_pending(struct timer_list *timer, unsigned long expires) EXPORT_SYMBOL(mod_timer_pending);
/** - * mod_timer - modify a timer's timeout - * @timer: the timer to be modified - * @expires: new timeout in jiffies - * - * mod_timer() is a more efficient way to update the expire field of an - * active timer (if the timer is inactive it will be activated) + * mod_timer - Modify a timer's timeout + * @timer: The timer to be modified + * @expires: New absolute timeout in jiffies * * mod_timer(timer, expires) is equivalent to: * * del_timer(timer); timer->expires = expires; add_timer(timer); * + * mod_timer() is more efficient than the above open coded sequence. In + * case that the timer is inactive, the del_timer() part is a NOP. The + * timer is in any case activated with the new expiry time @expires. + * * Note that if there are multiple unserialized concurrent users of the * same timer, then mod_timer() is the only safe way to modify the timeout, * since add_timer() cannot modify an already running timer. * - * The function returns whether it has modified a pending timer or not. - * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an - * active timer returns 1.) + * Return: + * * %0 - The timer was inactive and started + * * %1 - The timer was active and requeued to expire at @expires or + * the timer was active and not modified because @expires did + * not change the effective expiry time */ int mod_timer(struct timer_list *timer, unsigned long expires) { @@ -1112,11 +1117,18 @@ EXPORT_SYMBOL(mod_timer); /** * timer_reduce - Modify a timer's timeout if it would reduce the timeout * @timer: The timer to be modified - * @expires: New timeout in jiffies + * @expires: New absolute timeout in jiffies * * timer_reduce() is very similar to mod_timer(), except that it will only - * modify a running timer if that would reduce the expiration time (it will - * start a timer that isn't running). + * modify an enqueued timer if that would reduce the expiration time. If + * @timer is not enqueued it starts the timer. + * + * Return: + * * %0 - The timer was inactive and started + * * %1 - The timer was active and requeued to expire at @expires or + * the timer was active and not modified because @expires + * did not change the effective expiry time such that the + * timer would expire earlier than already scheduled */ int timer_reduce(struct timer_list *timer, unsigned long expires) { @@ -1125,18 +1137,21 @@ int timer_reduce(struct timer_list *timer, unsigned long expires) EXPORT_SYMBOL(timer_reduce);
/** - * add_timer - start a timer - * @timer: the timer to be added + * add_timer - Start a timer + * @timer: The timer to be started * - * The kernel will do a ->function(@timer) callback from the - * timer interrupt at the ->expires point in the future. The - * current time is 'jiffies'. + * Start @timer to expire at @timer->expires in the future. @timer->expires + * is the absolute expiry time measured in 'jiffies'. When the timer expires + * timer->function(timer) will be invoked from soft interrupt context. * - * The timer's ->expires, ->function fields must be set prior calling this - * function. + * The @timer->expires and @timer->function fields must be set prior + * to calling this function. + * + * If @timer->expires is already in the past @timer will be queued to + * expire at the next timer tick. * - * Timers with an ->expires field in the past will be executed in the next - * timer tick. + * This can only operate on an inactive timer. Attempts to invoke this on + * an active timer are rejected with a warning. */ void add_timer(struct timer_list *timer) { @@ -1147,11 +1162,13 @@ void add_timer(struct timer_list *timer) EXPORT_SYMBOL(add_timer);
/** - * add_timer_on - start a timer on a particular CPU - * @timer: the timer to be added - * @cpu: the CPU to start it on + * add_timer_on - Start a timer on a particular CPU + * @timer: The timer to be started + * @cpu: The CPU to start it on + * + * Same as add_timer() except that it starts the timer on the given CPU. * - * This is not very scalable on SMP. Double adds are not possible. + * See add_timer() for further details. */ void add_timer_on(struct timer_list *timer, int cpu) { @@ -1187,15 +1204,18 @@ void add_timer_on(struct timer_list *timer, int cpu) EXPORT_SYMBOL_GPL(add_timer_on);
/** - * del_timer - deactivate a timer. - * @timer: the timer to be deactivated - * - * del_timer() deactivates a timer - this works on both active and inactive - * timers. - * - * The function returns whether it has deactivated a pending timer or not. - * (ie. del_timer() of an inactive timer returns 0, del_timer() of an - * active timer returns 1.) + * del_timer - Deactivate a timer. + * @timer: The timer to be deactivated + * + * The function only deactivates a pending timer, but contrary to + * del_timer_sync() it does not take into account whether the timer's + * callback function is concurrently executed on a different CPU or not. + * It neither prevents rearming of the timer. If @timer can be rearmed + * concurrently then the return value of this function is meaningless. + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated */ int del_timer(struct timer_list *timer) { @@ -1217,10 +1237,19 @@ EXPORT_SYMBOL(del_timer);
/** * try_to_del_timer_sync - Try to deactivate a timer - * @timer: timer to delete + * @timer: Timer to deactivate + * + * This function tries to deactivate a timer. On success the timer is not + * queued and the timer callback function is not running on any CPU. * - * This function tries to deactivate a timer. Upon successful (ret >= 0) - * exit the timer is not queued and the handler is not running on any CPU. + * This function does not guarantee that the timer cannot be rearmed right + * after dropping the base lock. That needs to be prevented by the calling + * code if necessary. + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated + * * %-1 - The timer callback function is running on a different CPU */ int try_to_del_timer_sync(struct timer_list *timer) { @@ -1316,23 +1345,19 @@ static inline void del_timer_wait_running(struct timer_list *timer) { }
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT) /** - * del_timer_sync - deactivate a timer and wait for the handler to finish. - * @timer: the timer to be deactivated - * - * This function only differs from del_timer() on SMP: besides deactivating - * the timer it also makes sure the handler has finished executing on other - * CPUs. + * del_timer_sync - Deactivate a timer and wait for the handler to finish. + * @timer: The timer to be deactivated * * Synchronization rules: Callers must prevent restarting of the timer, * otherwise this function is meaningless. It must not be called from * interrupt contexts unless the timer is an irqsafe one. The caller must - * not hold locks which would prevent completion of the timer's - * handler. The timer's handler must not call add_timer_on(). Upon exit the - * timer is not queued and the handler is not running on any CPU. + * not hold locks which would prevent completion of the timer's callback + * function. The timer's handler must not call add_timer_on(). Upon exit + * the timer is not queued and the handler is not running on any CPU. * - * Note: For !irqsafe timers, you must not hold locks that are held in - * interrupt context while calling this function. Even if the lock has - * nothing to do with the timer in question. Here's why:: + * For !irqsafe timers, the caller must not hold locks that are held in + * interrupt context. Even if the lock has nothing to do with the timer in + * question. Here's why:: * * CPU0 CPU1 * ---- ---- @@ -1346,10 +1371,17 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } * while (base->running_timer == mytimer); * * Now del_timer_sync() will never return and never release somelock. - * The interrupt on the other CPU is waiting to grab somelock but - * it has interrupted the softirq that CPU0 is waiting to finish. + * The interrupt on the other CPU is waiting to grab somelock but it has + * interrupted the softirq that CPU0 is waiting to finish. + * + * This function cannot guarantee that the timer is not rearmed again by + * some concurrent or preempting code, right after it dropped the base + * lock. If there is the possibility of a concurrent rearm then the return + * value of the function is meaningless. * - * The function returns whether it has deactivated a pending timer or not. + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated */ int del_timer_sync(struct timer_list *timer) {
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 168f6b6ffbeec0b9333f3582e4cf637300858db5 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
del_timer_sync() is assumed to be pointless on uniprocessor systems and can be mapped to del_timer() because in theory del_timer() can never be invoked while the timer callback function is executed.
This is not entirely true because del_timer() can be invoked from interrupt context and therefore hit in the middle of a running timer callback.
Contrary to that del_timer_sync() is not allowed to be invoked from interrupt context unless the affected timer is marked with TIMER_IRQSAFE. del_timer_sync() has proper checks in place to detect such a situation.
Give up on the UP optimization and make del_timer_sync() unconditionally available.
Co-developed-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org Link: https://lore.kernel.org/r/20221123201624.888306160@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/timer.h | 7 +------ kernel/time/timer.c | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-)
diff --git a/include/linux/timer.h b/include/linux/timer.h index e7c9108f73a5..6fcf2837e308 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -189,12 +189,7 @@ extern int timer_reduce(struct timer_list *timer, unsigned long expires); extern void add_timer(struct timer_list *timer);
extern int try_to_del_timer_sync(struct timer_list *timer); - -#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT) - extern int del_timer_sync(struct timer_list *timer); -#else -# define del_timer_sync(t) del_timer(t) -#endif +extern int del_timer_sync(struct timer_list *timer);
extern void init_timers(void); extern void run_local_timers(void); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index cbec76a98ac0..46791b380802 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1343,7 +1343,6 @@ static inline void timer_sync_wait_running(struct timer_base *base) { } static inline void del_timer_wait_running(struct timer_list *timer) { } #endif
-#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT) /** * del_timer_sync - Deactivate a timer and wait for the handler to finish. * @timer: The timer to be deactivated @@ -1417,7 +1416,6 @@ int del_timer_sync(struct timer_list *timer) return ret; } EXPORT_SYMBOL(del_timer_sync); -#endif
static void call_timer_fn(struct timer_list *timer, void (*fn)(struct timer_list *),
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 9b13df3fb64ee95e2397585404e442afee2c7d4f category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The timer related functions do not have a strict timer_ prefixed namespace which is really annoying.
Rename del_timer_sync() to timer_delete_sync() and provide del_timer_sync() as a wrapper. Document that del_timer_sync() is not for new code.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Steven Rostedt (Google) rostedt@goodmis.org Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201624.954785441@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/timer.h | 15 ++++++++++++++- kernel/time/timer.c | 18 +++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/include/linux/timer.h b/include/linux/timer.h index 6fcf2837e308..ef62727c4e70 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -189,7 +189,20 @@ extern int timer_reduce(struct timer_list *timer, unsigned long expires); extern void add_timer(struct timer_list *timer);
extern int try_to_del_timer_sync(struct timer_list *timer); -extern int del_timer_sync(struct timer_list *timer); +extern int timer_delete_sync(struct timer_list *timer); + +/** + * del_timer_sync - Delete a pending timer and wait for a running callback + * @timer: The timer to be deleted + * + * See timer_delete_sync() for detailed explanation. + * + * Do not use in new code. Use timer_delete_sync() instead. + */ +static inline int del_timer_sync(struct timer_list *timer) +{ + return timer_delete_sync(timer); +}
extern void init_timers(void); extern void run_local_timers(void); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 46791b380802..7f4164c95abf 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1030,7 +1030,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option /* * We are trying to schedule the timer on the new base. * However we can't change timer's base while it is running, - * otherwise del_timer_sync() can't detect that the timer's + * otherwise timer_delete_sync() can't detect that the timer's * handler yet has not finished. This also guarantees that the * timer is serialized wrt itself. */ @@ -1208,7 +1208,7 @@ EXPORT_SYMBOL_GPL(add_timer_on); * @timer: The timer to be deactivated * * The function only deactivates a pending timer, but contrary to - * del_timer_sync() it does not take into account whether the timer's + * timer_delete_sync() it does not take into account whether the timer's * callback function is concurrently executed on a different CPU or not. * It neither prevents rearming of the timer. If @timer can be rearmed * concurrently then the return value of this function is meaningless. @@ -1344,7 +1344,7 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } #endif
/** - * del_timer_sync - Deactivate a timer and wait for the handler to finish. + * timer_delete_sync - Deactivate a timer and wait for the handler to finish. * @timer: The timer to be deactivated * * Synchronization rules: Callers must prevent restarting of the timer, @@ -1366,10 +1366,10 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } * spin_lock_irq(somelock); * <IRQ> * spin_lock(somelock); - * del_timer_sync(mytimer); + * timer_delete_sync(mytimer); * while (base->running_timer == mytimer); * - * Now del_timer_sync() will never return and never release somelock. + * Now timer_delete_sync() will never return and never release somelock. * The interrupt on the other CPU is waiting to grab somelock but it has * interrupted the softirq that CPU0 is waiting to finish. * @@ -1382,7 +1382,7 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated */ -int del_timer_sync(struct timer_list *timer) +int timer_delete_sync(struct timer_list *timer) { int ret;
@@ -1415,7 +1415,7 @@ int del_timer_sync(struct timer_list *timer)
return ret; } -EXPORT_SYMBOL(del_timer_sync); +EXPORT_SYMBOL(timer_delete_sync);
static void call_timer_fn(struct timer_list *timer, void (*fn)(struct timer_list *), @@ -1437,8 +1437,8 @@ static void call_timer_fn(struct timer_list *timer, #endif /* * Couple the lock chain with the lock chain at - * del_timer_sync() by acquiring the lock_map around the fn() - * call here and in del_timer_sync(). + * timer_delete_sync() by acquiring the lock_map around the fn() + * call here and in timer_delete_sync(). */ lock_map_acquire(&lockdep_map);
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit bb663f0f3c396c6d05f6c5eeeea96ced20ff112e category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The timer related functions do not have a strict timer_ prefixed namespace which is really annoying.
Rename del_timer() to timer_delete() and provide del_timer() as a wrapper. Document that del_timer() is not for new code.
Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Steven Rostedt (Google) rostedt@goodmis.org Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201625.015535022@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/timer.h | 15 ++++++++++++++- kernel/time/timer.c | 6 +++--- 2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/include/linux/timer.h b/include/linux/timer.h index ef62727c4e70..03970a42b352 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -175,7 +175,6 @@ static inline int timer_pending(const struct timer_list * timer) }
extern void add_timer_on(struct timer_list *timer, int cpu); -extern int del_timer(struct timer_list * timer); extern int mod_timer(struct timer_list *timer, unsigned long expires); extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); extern int timer_reduce(struct timer_list *timer, unsigned long expires); @@ -190,6 +189,7 @@ extern void add_timer(struct timer_list *timer);
extern int try_to_del_timer_sync(struct timer_list *timer); extern int timer_delete_sync(struct timer_list *timer); +extern int timer_delete(struct timer_list *timer);
/** * del_timer_sync - Delete a pending timer and wait for a running callback @@ -204,6 +204,19 @@ static inline int del_timer_sync(struct timer_list *timer) return timer_delete_sync(timer); }
+/** + * del_timer - Delete a pending timer + * @timer: The timer to be deleted + * + * See timer_delete() for detailed explanation. + * + * Do not use in new code. Use timer_delete() instead. + */ +static inline int del_timer(struct timer_list *timer) +{ + return timer_delete(timer); +} + extern void init_timers(void); extern void run_local_timers(void); struct hrtimer; diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 7f4164c95abf..76982f1244be 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1204,7 +1204,7 @@ void add_timer_on(struct timer_list *timer, int cpu) EXPORT_SYMBOL_GPL(add_timer_on);
/** - * del_timer - Deactivate a timer. + * timer_delete - Deactivate a timer * @timer: The timer to be deactivated * * The function only deactivates a pending timer, but contrary to @@ -1217,7 +1217,7 @@ EXPORT_SYMBOL_GPL(add_timer_on); * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated */ -int del_timer(struct timer_list *timer) +int timer_delete(struct timer_list *timer) { struct timer_base *base; unsigned long flags; @@ -1233,7 +1233,7 @@ int del_timer(struct timer_list *timer)
return ret; } -EXPORT_SYMBOL(del_timer); +EXPORT_SYMBOL(timer_delete);
/** * try_to_del_timer_sync - Try to deactivate a timer
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 87bdd932e85881895d4720255b40ac28749c4e32 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Adjust to the new preferred function names.
Suggested-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221123201625.075320635@linutronix.de
Conflicts: Documentation/RCU/Design/Requirements/Requirements.rst Documentation/translations/zh_CN/core-api/local_ops.rst Signed-off-by: Yu Liao liaoyu15@huawei.com --- .../RCU/Design/Requirements/Requirements.rst | 2 +- Documentation/core-api/local_ops.rst | 2 +- Documentation/kernel-hacking/locking.rst | 11 +- Documentation/timers/hrtimers.rst | 2 +- .../it_IT/kernel-hacking/locking.rst | 10 +- .../translations/zh_CN/core-api/local_ops.rst | 196 ++++++++++++++++++ 6 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 Documentation/translations/zh_CN/core-api/local_ops.rst
diff --git a/Documentation/RCU/Design/Requirements/Requirements.rst b/Documentation/RCU/Design/Requirements/Requirements.rst index 1ae79a10a8de..ad2cc20131ec 100644 --- a/Documentation/RCU/Design/Requirements/Requirements.rst +++ b/Documentation/RCU/Design/Requirements/Requirements.rst @@ -1858,7 +1858,7 @@ unloaded. After a given module has been unloaded, any attempt to call one of its functions results in a segmentation fault. The module-unload functions must therefore cancel any delayed calls to loadable-module functions, for example, any outstanding ``mod_timer()`` must be dealt -with via ``del_timer_sync()`` or similar. +with via ``timer_delete_sync()`` or similar.
Unfortunately, there is no way to cancel an RCU callback; once you invoke ``call_rcu()``, the callback function is eventually going to be diff --git a/Documentation/core-api/local_ops.rst b/Documentation/core-api/local_ops.rst index 2ac3f9f29845..a84f8b0c7ab2 100644 --- a/Documentation/core-api/local_ops.rst +++ b/Documentation/core-api/local_ops.rst @@ -191,7 +191,7 @@ Here is a sample module which implements a basic per cpu counter using
static void __exit test_exit(void) { - del_timer_sync(&test_timer); + timer_delete_sync(&test_timer); }
module_init(test_init); diff --git a/Documentation/kernel-hacking/locking.rst b/Documentation/kernel-hacking/locking.rst index d5ad37467d0c..20710efa0322 100644 --- a/Documentation/kernel-hacking/locking.rst +++ b/Documentation/kernel-hacking/locking.rst @@ -976,7 +976,7 @@ you might do the following::
while (list) { struct foo *next = list->next; - del_timer(&list->timer); + timer_delete(&list->timer); kfree(list); list = next; } @@ -990,7 +990,7 @@ the lock after we spin_unlock_bh(), and then try to free the element (which has already been freed!).
This can be avoided by checking the result of -del_timer(): if it returns 1, the timer has been deleted. +timer_delete(): if it returns 1, the timer has been deleted. If 0, it means (in this case) that it is currently running, so we can do::
@@ -999,7 +999,7 @@ do::
while (list) { struct foo *next = list->next; - if (!del_timer(&list->timer)) { + if (!timer_delete(&list->timer)) { /* Give timer a chance to delete this */ spin_unlock_bh(&list_lock); goto retry; @@ -1014,8 +1014,7 @@ do:: Another common problem is deleting timers which restart themselves (by calling add_timer() at the end of their timer function). Because this is a fairly common case which is prone to races, you should -use del_timer_sync() (``include/linux/timer.h``) to -handle this case. +use timer_delete_sync() (``include/linux/timer.h``) to handle this case.
Locking Speed ============= @@ -1343,7 +1342,7 @@ lock.
- kfree()
-- add_timer() and del_timer() +- add_timer() and timer_delete()
Mutex API reference =================== diff --git a/Documentation/timers/hrtimers.rst b/Documentation/timers/hrtimers.rst index c1c20a693e8f..7ac448908d1f 100644 --- a/Documentation/timers/hrtimers.rst +++ b/Documentation/timers/hrtimers.rst @@ -118,7 +118,7 @@ existing timer wheel code, as it is mature and well suited. Sharing code was not really a win, due to the different data structures. Also, the hrtimer functions now have clearer behavior and clearer names - such as hrtimer_try_to_cancel() and hrtimer_cancel() [which are roughly -equivalent to del_timer() and del_timer_sync()] - so there's no direct +equivalent to timer_delete() and timer_delete_sync()] - so there's no direct 1:1 mapping between them on the algorithmic level, and thus no real potential for code sharing either.
diff --git a/Documentation/translations/it_IT/kernel-hacking/locking.rst b/Documentation/translations/it_IT/kernel-hacking/locking.rst index 587ae37d2ee9..955864af913a 100644 --- a/Documentation/translations/it_IT/kernel-hacking/locking.rst +++ b/Documentation/translations/it_IT/kernel-hacking/locking.rst @@ -1000,7 +1000,7 @@ potreste fare come segue::
while (list) { struct foo *next = list->next; - del_timer(&list->timer); + timer_delete(&list->timer); kfree(list); list = next; } @@ -1013,7 +1013,7 @@ e prender�� il *lock* solo dopo spin_unlock_bh(), e cercher�� di eliminare il suo oggetto (che per�� �� gi�� stato eliminato).
Questo pu�� essere evitato controllando il valore di ritorno di -del_timer(): se ritorna 1, il temporizzatore �� stato gi�� +timer_delete(): se ritorna 1, il temporizzatore �� stato gi�� rimosso. Se 0, significa (in questo caso) che il temporizzatore �� in esecuzione, quindi possiamo fare come segue::
@@ -1022,7 +1022,7 @@ esecuzione, quindi possiamo fare come segue::
while (list) { struct foo *next = list->next; - if (!del_timer(&list->timer)) { + if (!timer_delete(&list->timer)) { /* Give timer a chance to delete this */ spin_unlock_bh(&list_lock); goto retry; @@ -1036,7 +1036,7 @@ esecuzione, quindi possiamo fare come segue:: Un altro problema �� l'eliminazione dei temporizzatori che si riavviano da soli (chiamando add_timer() alla fine della loro esecuzione). Dato che questo �� un problema abbastanza comune con una propensione -alle corse critiche, dovreste usare del_timer_sync() +alle corse critiche, dovreste usare timer_delete_sync() (``include/linux/timer.h``) per gestire questo caso.
Velocit�� della sincronizzazione @@ -1384,7 +1384,7 @@ contesto, o trattenendo un qualsiasi *lock*.
- kfree()
-- add_timer() e del_timer() +- add_timer() e timer_delete()
Riferimento per l'API dei Mutex =============================== diff --git a/Documentation/translations/zh_CN/core-api/local_ops.rst b/Documentation/translations/zh_CN/core-api/local_ops.rst new file mode 100644 index 000000000000..22493b9b829c --- /dev/null +++ b/Documentation/translations/zh_CN/core-api/local_ops.rst @@ -0,0 +1,196 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/core-api/local_ops.rst + +:������: + + ��������� Yanteng Si siyanteng@loongson.cn + +.. _cn_local_ops: + +======================== +������������������������������������ +======================== + +:������: Mathieu Desnoyers + + +������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������CPU������ +��������������������������������������������������� + +.. note:: + + ��������������� ``local_t`` ������������������������������������������������������ ``this_cpu`` + ��������������������������������������������������������������������������������� ``local_t`` ��� + ������ ``this_cpu`` ������������������ ``this_cpu`` ������������������������������������ + ��������������� ``local_t`` ��������������������������������������������������������� + + +��������������������������� +================== + +������������������������������������������������������������������CPU������������������������������LOCK��� +���������������������CPU������������������������������������������������������������������������������ + +���������������������������������������CPU��������������������������������������������������������������������������� +������������������������������NMI���Non Maskable Interrupt��������������������������������������������� +��������������������������������������������������������������� + +���������������������������������������������CPU��������������������������������������������������������������� +���������CPU������ ``local_t`` ���������������������������������CPU������������������������������ +���������������������������������������������������������������������������������CPU������ ``local_t`` +���������������������������������������������������������CPU��������������������������������������� + + +��������������������������� +================== + +���������������������������������������������������������������������������UP��������������������������������� +���������������LOCK������������i386���x86_64���������������SMP������������������������������SMP��� +UP������������������������������������������������ ``local.h`` ��������� ``asm-generic/local.h`` +��������������� + +��������������������������������������� ``atomic_long_t`` ��� ``local_t`` ������������������ +������������������ ``signed long`` ��������������������������������������������������� +``long`` ���������������������������������������:: + + typedef struct { atomic_long_t a; } local_t; + + +��������������������������������������������� +============================== + +* ������������������������������������������cpu������������ + +* *������* ���������������CPU��������������������������������������� + +* ������CPU���������������������������������������������������������nmi...��������������������������������� + ������local_t��������� + +* ��������������������������������������������������������������������������������������������������������������� + CPU���������������������������������������������������������������������CPU��� + +* ��������������������������������������������������������������������������������������������������������������� + ���CPU������������������������������������������������������������������������������������������������������ + ���������������-rt������������������������������ + +* ������������cpu������������������������������������������ + +* ���������������������������������������CPU������������������ ��� ``long`` ������������������������������ + ���������������������������������������CPU������������������������������������������ *������* cpu������ + ������������������������������������������������ + + +������������������������������ +==================== + +:: + + #include <linux/percpu.h> + #include <asm/local.h> + + static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0); + + +��������� +====== + +������������������signed long��������������������������� + +������������������������������������������������������������ ``get_cpu_var()`` ��� +``put_cpu_var()`` ������������������������cpu��������������������������������������������������� +���:: + + local_inc(&get_cpu_var(counters)); + put_cpu_var(counters); + +������������������������������������������������������������������ ``this_cpu_ptr()`` ������:: + + local_inc(this_cpu_ptr(&counters)); + + + +��������������� +========== + +���������������������������������������CPU���������������������������������������������������local_read +���������������CPU������������������������������������������������������CPU��������������������������������� +���������������������:: + + long sum = 0; + for_each_online_cpu(cpu) + sum += local_read(&per_cpu(counters, cpu)); + +������������������������local_read���������CPU��������������������������������������������������������� +���CPU������������������������ ``smp_wmb()`` ��� ``smp_rmb()`` ��������������������������� +��� ``local_t`` ��������������������������������������������������������������������������������������� +������������������������������������������������ ``smp_wmb()`` ��������������������������������������� +��������������� ``smp_rmb()`` ��� + +��������������������� ``local.h`` ������������cpu������������������������������:: + + /* test-local.c + * + * Sample module for local.h usage. + */ + + + #include <asm/local.h> + #include <linux/module.h> + #include <linux/timer.h> + + static DEFINE_PER_CPU(local_t, counters) = LOCAL_INIT(0); + + static struct timer_list test_timer; + + /* IPI called on each CPU. */ + static void test_each(void *info) + { + /* Increment the counter from a non preemptible context */ + printk("Increment on cpu %d\n", smp_processor_id()); + local_inc(this_cpu_ptr(&counters)); + + /* This is what incrementing the variable would look like within a + * preemptible context (it disables preemption) : + * + * local_inc(&get_cpu_var(counters)); + * put_cpu_var(counters); + */ + } + + static void do_test_timer(unsigned long data) + { + int cpu; + + /* Increment the counters */ + on_each_cpu(test_each, NULL, 1); + /* Read all the counters */ + printk("Counters read from CPU %d\n", smp_processor_id()); + for_each_online_cpu(cpu) { + printk("Read : CPU %d, count %ld\n", cpu, + local_read(&per_cpu(counters, cpu))); + } + mod_timer(&test_timer, jiffies + 1000); + } + + static int __init test_init(void) + { + /* initialize the timer that will increment the counter */ + timer_setup(&test_timer, do_test_timer, 0); + mod_timer(&test_timer, jiffies + 1); + + return 0; + } + + static void __exit test_exit(void) + { + timer_delete_sync(&test_timer); + } + + module_init(test_init); + module_exit(test_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Mathieu Desnoyers"); + MODULE_DESCRIPTION("Local Atomic Ops");
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit d02e382cef06cc73561dd32dfdc171c00dcc416d category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Tearing down timers which have circular dependencies to other functionality, e.g. workqueues, where the timer can schedule work and work can arm timers, is not trivial.
In those cases it is desired to shutdown the timer in a way which prevents rearming of the timer. The mechanism to do so is to set timer->function to NULL and use this as an indicator for the timer arming functions to ignore the (re)arm request.
In preparation for that replace the warnings in the relevant code paths with checks for timer->function == NULL. If the pointer is NULL, then discard the rearm request silently.
Add debug_assert_init() instead of the WARN_ON_ONCE(!timer->function) checks so that debug objects can warn about non-initialized timers.
The warning of debug objects does not warn if timer->function == NULL. It warns when timer was not initialized using timer_setup[_on_stack]() or via DEFINE_TIMER(). If developers fail to enable debug objects and then waste lots of time to figure out why their non-initialized timer is not firing, they deserve it. Same for initializing a timer with a NULL function.
Co-developed-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org Link: https://lore.kernel.org/r/87wn7kdann.ffs@tglx
Signed-off-by: Yu Liao liaoyu15@huawei.com --- kernel/time/timer.c | 57 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 76982f1244be..e732ae69da7f 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -964,7 +964,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option unsigned int idx = UINT_MAX; int ret = 0;
- BUG_ON(!timer->function); + debug_assert_init(timer);
/* * This is a common optimization triggered by the networking code - if @@ -991,6 +991,14 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option * dequeue/enqueue dance. */ base = lock_timer_base(timer, &flags); + /* + * Has @timer been shutdown? This needs to be evaluated + * while holding base lock to prevent a race against the + * shutdown code. + */ + if (!timer->function) + goto out_unlock; + forward_timer_base(base);
if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) && @@ -1017,6 +1025,14 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option } } else { base = lock_timer_base(timer, &flags); + /* + * Has @timer been shutdown? This needs to be evaluated + * while holding base lock to prevent a race against the + * shutdown code. + */ + if (!timer->function) + goto out_unlock; + forward_timer_base(base); }
@@ -1075,8 +1091,12 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option * mod_timer_pending() is the same for pending timers as mod_timer(), but * will not activate inactive timers. * + * If @timer->function == NULL then the start operation is silently + * discarded. + * * Return: - * * %0 - The timer was inactive and not modified + * * %0 - The timer was inactive and not modified or was in + * shutdown state and the operation was discarded * * %1 - The timer was active and requeued to expire at @expires */ int mod_timer_pending(struct timer_list *timer, unsigned long expires) @@ -1102,8 +1122,12 @@ EXPORT_SYMBOL(mod_timer_pending); * same timer, then mod_timer() is the only safe way to modify the timeout, * since add_timer() cannot modify an already running timer. * + * If @timer->function == NULL then the start operation is silently + * discarded. In this case the return value is 0 and meaningless. + * * Return: - * * %0 - The timer was inactive and started + * * %0 - The timer was inactive and started or was in shutdown + * state and the operation was discarded * * %1 - The timer was active and requeued to expire at @expires or * the timer was active and not modified because @expires did * not change the effective expiry time @@ -1123,8 +1147,12 @@ EXPORT_SYMBOL(mod_timer); * modify an enqueued timer if that would reduce the expiration time. If * @timer is not enqueued it starts the timer. * + * If @timer->function == NULL then the start operation is silently + * discarded. + * * Return: - * * %0 - The timer was inactive and started + * * %0 - The timer was inactive and started or was in shutdown + * state and the operation was discarded * * %1 - The timer was active and requeued to expire at @expires or * the timer was active and not modified because @expires * did not change the effective expiry time such that the @@ -1147,6 +1175,9 @@ EXPORT_SYMBOL(timer_reduce); * The @timer->expires and @timer->function fields must be set prior * to calling this function. * + * If @timer->function == NULL then the start operation is silently + * discarded. + * * If @timer->expires is already in the past @timer will be queued to * expire at the next timer tick. * @@ -1175,7 +1206,9 @@ void add_timer_on(struct timer_list *timer, int cpu) struct timer_base *new_base, *base; unsigned long flags;
- if (WARN_ON_ONCE(timer_pending(timer) || !timer->function)) + debug_assert_init(timer); + + if (WARN_ON_ONCE(timer_pending(timer))) return;
new_base = get_timer_cpu_base(timer->flags, cpu); @@ -1186,6 +1219,13 @@ void add_timer_on(struct timer_list *timer, int cpu) * wrong base locked. See lock_timer_base(). */ base = lock_timer_base(timer, &flags); + /* + * Has @timer been shutdown? This needs to be evaluated while + * holding base lock to prevent a race against the shutdown code. + */ + if (!timer->function) + goto out_unlock; + if (base != new_base) { timer->flags |= TIMER_MIGRATING;
@@ -1199,6 +1239,7 @@ void add_timer_on(struct timer_list *timer, int cpu)
debug_timer_activate(timer); internal_add_timer(base, timer); +out_unlock: raw_spin_unlock_irqrestore(&base->lock, flags); } EXPORT_SYMBOL_GPL(add_timer_on); @@ -1481,6 +1522,12 @@ static void expire_timers(struct timer_base *base, struct hlist_head *head)
fn = timer->function;
+ if (WARN_ON_ONCE(!fn)) { + /* Should never happen. Emphasis on should! */ + base->running_timer = NULL; + continue; + } + if (timer->flags & TIMER_IRQSAFE) { raw_spin_unlock(&base->lock); call_timer_fn(timer, fn, baseclk);
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 8553b5f2774a66b1f293b7d783934210afb8f23c category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Tearing down timers which have circular dependencies to other functionality, e.g. workqueues, where the timer can schedule work and work can arm timers, is not trivial.
In those cases it is desired to shutdown the timer in a way which prevents rearming of the timer. The mechanism to do so is to set timer->function to NULL and use this as an indicator for the timer arming functions to ignore the (re)arm request.
Split the inner workings of try_do_del_timer_sync(), del_timer_sync() and del_timer() into helper functions to prepare for implementing the shutdown functionality.
No functional change.
Co-developed-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org Link: https://lore.kernel.org/r/20221123201625.195147423@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- kernel/time/timer.c | 135 +++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 47 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index e732ae69da7f..55b90df70b00 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1245,20 +1245,14 @@ void add_timer_on(struct timer_list *timer, int cpu) EXPORT_SYMBOL_GPL(add_timer_on);
/** - * timer_delete - Deactivate a timer + * __timer_delete - Internal function: Deactivate a timer * @timer: The timer to be deactivated * - * The function only deactivates a pending timer, but contrary to - * timer_delete_sync() it does not take into account whether the timer's - * callback function is concurrently executed on a different CPU or not. - * It neither prevents rearming of the timer. If @timer can be rearmed - * concurrently then the return value of this function is meaningless. - * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated */ -int timer_delete(struct timer_list *timer) +static int __timer_delete(struct timer_list *timer) { struct timer_base *base; unsigned long flags; @@ -1274,25 +1268,37 @@ int timer_delete(struct timer_list *timer)
return ret; } -EXPORT_SYMBOL(timer_delete);
/** - * try_to_del_timer_sync - Try to deactivate a timer - * @timer: Timer to deactivate + * timer_delete - Deactivate a timer + * @timer: The timer to be deactivated * - * This function tries to deactivate a timer. On success the timer is not - * queued and the timer callback function is not running on any CPU. + * The function only deactivates a pending timer, but contrary to + * timer_delete_sync() it does not take into account whether the timer's + * callback function is concurrently executed on a different CPU or not. + * It neither prevents rearming of the timer. If @timer can be rearmed + * concurrently then the return value of this function is meaningless. * - * This function does not guarantee that the timer cannot be rearmed right - * after dropping the base lock. That needs to be prevented by the calling - * code if necessary. + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated + */ +int timer_delete(struct timer_list *timer) +{ + return __timer_delete(timer); +} +EXPORT_SYMBOL(timer_delete); + +/** + * __try_to_del_timer_sync - Internal function: Try to deactivate a timer + * @timer: Timer to deactivate * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated * * %-1 - The timer callback function is running on a different CPU */ -int try_to_del_timer_sync(struct timer_list *timer) +static int __try_to_del_timer_sync(struct timer_list *timer) { struct timer_base *base; unsigned long flags; @@ -1309,6 +1315,27 @@ int try_to_del_timer_sync(struct timer_list *timer)
return ret; } + +/** + * try_to_del_timer_sync - Try to deactivate a timer + * @timer: Timer to deactivate + * + * This function tries to deactivate a timer. On success the timer is not + * queued and the timer callback function is not running on any CPU. + * + * This function does not guarantee that the timer cannot be rearmed right + * after dropping the base lock. That needs to be prevented by the calling + * code if necessary. + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated + * * %-1 - The timer callback function is running on a different CPU + */ +int try_to_del_timer_sync(struct timer_list *timer) +{ + return __try_to_del_timer_sync(timer); +} EXPORT_SYMBOL(try_to_del_timer_sync);
#ifdef CONFIG_PREEMPT_RT @@ -1384,6 +1411,49 @@ static inline void timer_sync_wait_running(struct timer_base *base) { } static inline void del_timer_wait_running(struct timer_list *timer) { } #endif
+/** + * __timer_delete_sync - Internal function: Deactivate a timer and wait + * for the handler to finish. + * @timer: The timer to be deactivated + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending and deactivated + */ +static int __timer_delete_sync(struct timer_list *timer) +{ + int ret; + +#ifdef CONFIG_LOCKDEP + unsigned long flags; + + /* + * If lockdep gives a backtrace here, please reference + * the synchronization rules above. + */ + local_irq_save(flags); + lock_map_acquire(&timer->lockdep_map); + lock_map_release(&timer->lockdep_map); + local_irq_restore(flags); +#endif + /* + * don't use it in hardirq context, because it + * could lead to deadlock. + */ + WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE)); + + do { + ret = __try_to_del_timer_sync(timer); + + if (unlikely(ret < 0)) { + del_timer_wait_running(timer); + cpu_relax(); + } + } while (ret < 0); + + return ret; +} + /** * timer_delete_sync - Deactivate a timer and wait for the handler to finish. * @timer: The timer to be deactivated @@ -1425,36 +1495,7 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } */ int timer_delete_sync(struct timer_list *timer) { - int ret; - -#ifdef CONFIG_LOCKDEP - unsigned long flags; - - /* - * If lockdep gives a backtrace here, please reference - * the synchronization rules above. - */ - local_irq_save(flags); - lock_map_acquire(&timer->lockdep_map); - lock_map_release(&timer->lockdep_map); - local_irq_restore(flags); -#endif - /* - * don't use it in hardirq context, because it - * could lead to deadlock. - */ - WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE)); - - do { - ret = try_to_del_timer_sync(timer); - - if (unlikely(ret < 0)) { - del_timer_wait_running(timer); - cpu_relax(); - } - } while (ret < 0); - - return ret; + return __timer_delete_sync(timer); } EXPORT_SYMBOL(timer_delete_sync);
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit 0cc04e80458a822300b93f82ed861a513edde194 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Tearing down timers which have circular dependencies to other functionality, e.g. workqueues, where the timer can schedule work and work can arm timers, is not trivial.
In those cases it is desired to shutdown the timer in a way which prevents rearming of the timer. The mechanism to do so is to set timer->function to NULL and use this as an indicator for the timer arming functions to ignore the (re)arm request.
Add a shutdown argument to the relevant internal functions which makes the actual deactivation code set timer->function to NULL which in turn prevents rearming of the timer.
Co-developed-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org Link: https://lore.kernel.org/r/20221123201625.253883224@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- kernel/time/timer.c | 62 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 55b90df70b00..9901ae10b31a 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1247,12 +1247,19 @@ EXPORT_SYMBOL_GPL(add_timer_on); /** * __timer_delete - Internal function: Deactivate a timer * @timer: The timer to be deactivated + * @shutdown: If true, this indicates that the timer is about to be + * shutdown permanently. + * + * If @shutdown is true then @timer->function is set to NULL under the + * timer base lock which prevents further rearming of the time. In that + * case any attempt to rearm @timer after this function returns will be + * silently ignored. * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated */ -static int __timer_delete(struct timer_list *timer) +static int __timer_delete(struct timer_list *timer, bool shutdown) { struct timer_base *base; unsigned long flags; @@ -1260,9 +1267,22 @@ static int __timer_delete(struct timer_list *timer)
debug_assert_init(timer);
- if (timer_pending(timer)) { + /* + * If @shutdown is set then the lock has to be taken whether the + * timer is pending or not to protect against a concurrent rearm + * which might hit between the lockless pending check and the lock + * aquisition. By taking the lock it is ensured that such a newly + * enqueued timer is dequeued and cannot end up with + * timer->function == NULL in the expiry code. + * + * If timer->function is currently executed, then this makes sure + * that the callback cannot requeue the timer. + */ + if (timer_pending(timer) || shutdown) { base = lock_timer_base(timer, &flags); ret = detach_if_pending(timer, base, true); + if (shutdown) + timer->function = NULL; raw_spin_unlock_irqrestore(&base->lock, flags); }
@@ -1285,20 +1305,31 @@ static int __timer_delete(struct timer_list *timer) */ int timer_delete(struct timer_list *timer) { - return __timer_delete(timer); + return __timer_delete(timer, false); } EXPORT_SYMBOL(timer_delete);
/** * __try_to_del_timer_sync - Internal function: Try to deactivate a timer * @timer: Timer to deactivate + * @shutdown: If true, this indicates that the timer is about to be + * shutdown permanently. + * + * If @shutdown is true then @timer->function is set to NULL under the + * timer base lock which prevents further rearming of the timer. Any + * attempt to rearm @timer after this function returns will be silently + * ignored. + * + * This function cannot guarantee that the timer cannot be rearmed + * right after dropping the base lock if @shutdown is false. That + * needs to be prevented by the calling code if necessary. * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated * * %-1 - The timer callback function is running on a different CPU */ -static int __try_to_del_timer_sync(struct timer_list *timer) +static int __try_to_del_timer_sync(struct timer_list *timer, bool shutdown) { struct timer_base *base; unsigned long flags; @@ -1310,6 +1341,8 @@ static int __try_to_del_timer_sync(struct timer_list *timer)
if (base->running_timer != timer) ret = detach_if_pending(timer, base, true); + if (shutdown) + timer->function = NULL;
raw_spin_unlock_irqrestore(&base->lock, flags);
@@ -1334,7 +1367,7 @@ static int __try_to_del_timer_sync(struct timer_list *timer) */ int try_to_del_timer_sync(struct timer_list *timer) { - return __try_to_del_timer_sync(timer); + return __try_to_del_timer_sync(timer, false); } EXPORT_SYMBOL(try_to_del_timer_sync);
@@ -1415,12 +1448,25 @@ static inline void del_timer_wait_running(struct timer_list *timer) { } * __timer_delete_sync - Internal function: Deactivate a timer and wait * for the handler to finish. * @timer: The timer to be deactivated + * @shutdown: If true, @timer->function will be set to NULL under the + * timer base lock which prevents rearming of @timer + * + * If @shutdown is not set the timer can be rearmed later. If the timer can + * be rearmed concurrently, i.e. after dropping the base lock then the + * return value is meaningless. + * + * If @shutdown is set then @timer->function is set to NULL under timer + * base lock which prevents rearming of the timer. Any attempt to rearm + * a shutdown timer is silently ignored. + * + * If the timer should be reused after shutdown it has to be initialized + * again. * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated */ -static int __timer_delete_sync(struct timer_list *timer) +static int __timer_delete_sync(struct timer_list *timer, bool shutdown) { int ret;
@@ -1443,7 +1489,7 @@ static int __timer_delete_sync(struct timer_list *timer) WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
do { - ret = __try_to_del_timer_sync(timer); + ret = __try_to_del_timer_sync(timer, shutdown);
if (unlikely(ret < 0)) { del_timer_wait_running(timer); @@ -1495,7 +1541,7 @@ static int __timer_delete_sync(struct timer_list *timer) */ int timer_delete_sync(struct timer_list *timer) { - return __timer_delete_sync(timer); + return __timer_delete_sync(timer, false); } EXPORT_SYMBOL(timer_delete_sync);
From: Thomas Gleixner tglx@linutronix.de
mainline inclusion from mainline-v6.2-rc1 commit f571faf6e443b6011ccb585d57866177af1f643c category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
Tearing down timers which have circular dependencies to other functionality, e.g. workqueues, where the timer can schedule work and work can arm timers, is not trivial.
In those cases it is desired to shutdown the timer in a way which prevents rearming of the timer. The mechanism to do so is to set timer->function to NULL and use this as an indicator for the timer arming functions to ignore the (re)arm request.
Expose new interfaces for this: timer_shutdown_sync() and timer_shutdown().
timer_shutdown_sync() has the same functionality as timer_delete_sync() plus the NULL-ification of the timer function.
timer_shutdown() has the same functionality as timer_delete() plus the NULL-ification of the timer function.
In both cases the rearming of the timer is prevented by silently discarding rearm attempts due to timer->function being NULL.
Co-developed-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Steven Rostedt rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/all/20220407161745.7d6754b3@gandalf.local.home Link: https://lore.kernel.org/all/20221110064101.429013735@goodmis.org Link: https://lore.kernel.org/r/20221123201625.314230270@linutronix.de
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/timer.h | 2 ++ kernel/time/timer.c | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+)
diff --git a/include/linux/timer.h b/include/linux/timer.h index 03970a42b352..e095f11cf665 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -190,6 +190,8 @@ extern void add_timer(struct timer_list *timer); extern int try_to_del_timer_sync(struct timer_list *timer); extern int timer_delete_sync(struct timer_list *timer); extern int timer_delete(struct timer_list *timer); +extern int timer_shutdown_sync(struct timer_list *timer); +extern int timer_shutdown(struct timer_list *timer);
/** * del_timer_sync - Delete a pending timer and wait for a running callback diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 9901ae10b31a..14c48a0144ea 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1309,6 +1309,27 @@ int timer_delete(struct timer_list *timer) } EXPORT_SYMBOL(timer_delete);
+/** + * timer_shutdown - Deactivate a timer and prevent rearming + * @timer: The timer to be deactivated + * + * The function does not wait for an eventually running timer callback on a + * different CPU but it prevents rearming of the timer. Any attempt to arm + * @timer after this function returns will be silently ignored. + * + * This function is useful for teardown code and should only be used when + * timer_shutdown_sync() cannot be invoked due to locking or context constraints. + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending + */ +int timer_shutdown(struct timer_list *timer) +{ + return __timer_delete(timer, true); +} +EXPORT_SYMBOL_GPL(timer_shutdown); + /** * __try_to_del_timer_sync - Internal function: Try to deactivate a timer * @timer: Timer to deactivate @@ -1535,6 +1556,9 @@ static int __timer_delete_sync(struct timer_list *timer, bool shutdown) * lock. If there is the possibility of a concurrent rearm then the return * value of the function is meaningless. * + * If such a guarantee is needed, e.g. for teardown situations then use + * timer_shutdown_sync() instead. + * * Return: * * %0 - The timer was not pending * * %1 - The timer was pending and deactivated @@ -1545,6 +1569,48 @@ int timer_delete_sync(struct timer_list *timer) } EXPORT_SYMBOL(timer_delete_sync);
+/** + * timer_shutdown_sync - Shutdown a timer and prevent rearming + * @timer: The timer to be shutdown + * + * When the function returns it is guaranteed that: + * - @timer is not queued + * - The callback function of @timer is not running + * - @timer cannot be enqueued again. Any attempt to rearm + * @timer is silently ignored. + * + * See timer_delete_sync() for synchronization rules. + * + * This function is useful for final teardown of an infrastructure where + * the timer is subject to a circular dependency problem. + * + * A common pattern for this is a timer and a workqueue where the timer can + * schedule work and work can arm the timer. On shutdown the workqueue must + * be destroyed and the timer must be prevented from rearming. Unless the + * code has conditionals like 'if (mything->in_shutdown)' to prevent that + * there is no way to get this correct with timer_delete_sync(). + * + * timer_shutdown_sync() is solving the problem. The correct ordering of + * calls in this case is: + * + * timer_shutdown_sync(&mything->timer); + * workqueue_destroy(&mything->workqueue); + * + * After this 'mything' can be safely freed. + * + * This obviously implies that the timer is not required to be functional + * for the rest of the shutdown operation. + * + * Return: + * * %0 - The timer was not pending + * * %1 - The timer was pending + */ +int timer_shutdown_sync(struct timer_list *timer) +{ + return __timer_delete_sync(timer, true); +} +EXPORT_SYMBOL_GPL(timer_shutdown_sync); + static void call_timer_fn(struct timer_list *timer, void (*fn)(struct timer_list *), unsigned long baseclk)
From: "Steven Rostedt (Google)" rostedt@goodmis.org
mainline inclusion from mainline-v6.2-rc1 commit a31323bef2b66455920d054b160c17d4240f8fd4 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
In order to make sure that a timer is not re-armed after it is stopped before freeing, a new shutdown state is added to the timer code. The API timer_shutdown_sync() and timer_shutdown() must be called before the object that holds the timer can be freed.
Update the documentation to reflect this new workflow.
[ tglx: Updated to the new semantics and updated the zh_CN version ]
Signed-off-by: Steven Rostedt (Google) rostedt@goodmis.org Signed-off-by: Thomas Gleixner tglx@linutronix.de Tested-by: Guenter Roeck linux@roeck-us.net Reviewed-by: Jacob Keller jacob.e.keller@intel.com Reviewed-by: Anna-Maria Behnsen anna-maria@linutronix.de Link: https://lore.kernel.org/r/20221110064147.712934793@goodmis.org Link: https://lore.kernel.org/r/20221123201625.375284489@linutronix.de
Conflicts: Documentation/RCU/Design/Requirements/Requirements.rst Signed-off-by: Yu Liao liaoyu15@huawei.com --- Documentation/RCU/Design/Requirements/Requirements.rst | 2 +- Documentation/core-api/local_ops.rst | 2 +- Documentation/kernel-hacking/locking.rst | 5 +++++ Documentation/translations/zh_CN/core-api/local_ops.rst | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/Documentation/RCU/Design/Requirements/Requirements.rst b/Documentation/RCU/Design/Requirements/Requirements.rst index ad2cc20131ec..e8af3dc3c95a 100644 --- a/Documentation/RCU/Design/Requirements/Requirements.rst +++ b/Documentation/RCU/Design/Requirements/Requirements.rst @@ -1858,7 +1858,7 @@ unloaded. After a given module has been unloaded, any attempt to call one of its functions results in a segmentation fault. The module-unload functions must therefore cancel any delayed calls to loadable-module functions, for example, any outstanding ``mod_timer()`` must be dealt -with via ``timer_delete_sync()`` or similar. +with via ``timer_shutdown_sync()`` or similar.
Unfortunately, there is no way to cancel an RCU callback; once you invoke ``call_rcu()``, the callback function is eventually going to be diff --git a/Documentation/core-api/local_ops.rst b/Documentation/core-api/local_ops.rst index a84f8b0c7ab2..0b42ceaaf3c4 100644 --- a/Documentation/core-api/local_ops.rst +++ b/Documentation/core-api/local_ops.rst @@ -191,7 +191,7 @@ Here is a sample module which implements a basic per cpu counter using
static void __exit test_exit(void) { - timer_delete_sync(&test_timer); + timer_shutdown_sync(&test_timer); }
module_init(test_init); diff --git a/Documentation/kernel-hacking/locking.rst b/Documentation/kernel-hacking/locking.rst index 20710efa0322..a9cf9b233b77 100644 --- a/Documentation/kernel-hacking/locking.rst +++ b/Documentation/kernel-hacking/locking.rst @@ -1016,6 +1016,11 @@ calling add_timer() at the end of their timer function). Because this is a fairly common case which is prone to races, you should use timer_delete_sync() (``include/linux/timer.h``) to handle this case.
+Before freeing a timer, timer_shutdown() or timer_shutdown_sync() should be +called which will keep it from being rearmed. Any subsequent attempt to +rearm the timer will be silently ignored by the core code. + + Locking Speed =============
diff --git a/Documentation/translations/zh_CN/core-api/local_ops.rst b/Documentation/translations/zh_CN/core-api/local_ops.rst index 22493b9b829c..eb5423f60f17 100644 --- a/Documentation/translations/zh_CN/core-api/local_ops.rst +++ b/Documentation/translations/zh_CN/core-api/local_ops.rst @@ -185,7 +185,7 @@ UP������������������������������������������������ ``local.h`` ��������� ``asm-g
static void __exit test_exit(void) { - timer_delete_sync(&test_timer); + timer_shutdown_sync(&test_timer); }
module_init(test_init);
Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7R8WG
--------------------------------
A previous commit made del_timer[_sync]() obsolete and unexported, which caused kabi to break. So making del_timer[_sync]() exported, the same as before.
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/timer.h | 28 ++-------------------------- kernel/time/timer.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/include/linux/timer.h b/include/linux/timer.h index e095f11cf665..13fc6e688c73 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -190,35 +190,11 @@ extern void add_timer(struct timer_list *timer); extern int try_to_del_timer_sync(struct timer_list *timer); extern int timer_delete_sync(struct timer_list *timer); extern int timer_delete(struct timer_list *timer); +extern int del_timer_sync(struct timer_list *timer); +extern int del_timer(struct timer_list *timer); extern int timer_shutdown_sync(struct timer_list *timer); extern int timer_shutdown(struct timer_list *timer);
-/** - * del_timer_sync - Delete a pending timer and wait for a running callback - * @timer: The timer to be deleted - * - * See timer_delete_sync() for detailed explanation. - * - * Do not use in new code. Use timer_delete_sync() instead. - */ -static inline int del_timer_sync(struct timer_list *timer) -{ - return timer_delete_sync(timer); -} - -/** - * del_timer - Delete a pending timer - * @timer: The timer to be deleted - * - * See timer_delete() for detailed explanation. - * - * Do not use in new code. Use timer_delete() instead. - */ -static inline int del_timer(struct timer_list *timer) -{ - return timer_delete(timer); -} - extern void init_timers(void); extern void run_local_timers(void); struct hrtimer; diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 14c48a0144ea..c1b52dab3951 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1309,6 +1309,20 @@ int timer_delete(struct timer_list *timer) } EXPORT_SYMBOL(timer_delete);
+/** + * del_timer - Delete a pending timer + * @timer: The timer to be deleted + * + * See timer_delete() for detailed explanation. + * + * Do not use in new code. Use timer_delete() instead. + */ +int del_timer(struct timer_list *timer) +{ + return __timer_delete(timer, false); +} +EXPORT_SYMBOL(del_timer); + /** * timer_shutdown - Deactivate a timer and prevent rearming * @timer: The timer to be deactivated @@ -1569,6 +1583,20 @@ int timer_delete_sync(struct timer_list *timer) } EXPORT_SYMBOL(timer_delete_sync);
+/** + * del_timer_sync - Delete a pending timer and wait for a running callback + * @timer: The timer to be deleted + * + * See timer_delete_sync() for detailed explanation. + * + * Do not use in new code. Use timer_delete_sync() instead. + */ +int del_timer_sync(struct timer_list *timer) +{ + return __timer_delete_sync(timer, false); +} +EXPORT_SYMBOL(del_timer_sync); + /** * timer_shutdown_sync - Shutdown a timer and prevent rearming * @timer: The timer to be shutdown