History

entry: Prepare for deferred hrtimer rearming

The hrtimer interrupt expires timers and at the end of the interrupt it rearms the clockevent device for the next expiring timer. That's obviously correct, but in the case that a expired timer sets NEED_RESCHED the return from interrupt ends up in schedule(). If HRTICK is enabled then schedule() will modify the hrtick timer, which causes another reprogramming of the hardware. That can be avoided by deferring the rearming to the return from interrupt path and if the return results in a immediate schedule() invocation then it can be deferred until the end of schedule(), which avoids multiple rearms and re-evaluation of the timer wheel. As this is only relevant for interrupt to user return split the work masks up and hand them in as arguments from the relevant exit to user functions, which allows the compiler to optimize the deferred handling out for the syscall exit to user case. Add the rearm checks to the approritate places in the exit to user loop and the interrupt return to kernel path, so that the rearming is always guaranteed. In the return to user space path this is handled in the same way as TIF_RSEQ to avoid extra instructions in the fast path, which are truly hurtful for device interrupt heavy work loads as the extra instructions and conditionals while benign at first sight accumulate quickly into measurable regressions. The return from syscall path is completely unaffected due to the above mentioned split so syscall heavy workloads wont have any extra burden. For now this is just placing empty stubs at the right places which are all optimized out by the compiler until the actual functionality is in place.

Peter Zijlstra committed 8 months ago

Commit 0e98eb11 parent a43b4853 files · +35 −10Browse files

Unified · Split

3 files
  1. include/linux/irq-entry-common.h +19 −6
  2. include/linux/rseq_entry.h +13 −3
  3. kernel/entry/common.c +3 −1
include/linux/irq-entry-common.h+19−6
@@ -3,6 +3,7 @@ · 2 unmodified lines
3#define __LINUX_IRQENTRYCOMMON_H3#define __LINUX_IRQENTRYCOMMON_H
4 4
5#include <linux/context_tracking.h>5#include <linux/context_tracking.h>
6#include <linux/hrtimer_rearm.h>
6#include <linux/kmsan.h>7#include <linux/kmsan.h>
7#include <linux/rseq_entry.h>8#include <linux/rseq_entry.h>
8#include <linux/static_call_types.h>9#include <linux/static_call_types.h>
@@ -33,6 +34,14 @@ · 24 unmodified lines
33 _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | _TIF_RSEQ | \34 _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | _TIF_RSEQ | \
34 ARCH_EXIT_TO_USER_MODE_WORK)35 ARCH_EXIT_TO_USER_MODE_WORK)
35 36
37#ifdef CONFIG_HRTIMER_REARM_DEFERRED
38# define EXIT_TO_USER_MODE_WORK_SYSCALL (EXIT_TO_USER_MODE_WORK)
39# define EXIT_TO_USER_MODE_WORK_IRQ (EXIT_TO_USER_MODE_WORK | _TIF_HRTIMER_REARM)
40#else
41# define EXIT_TO_USER_MODE_WORK_SYSCALL (EXIT_TO_USER_MODE_WORK)
42# define EXIT_TO_USER_MODE_WORK_IRQ (EXIT_TO_USER_MODE_WORK)
43#endif
44
36/**45/**
37 * arch_enter_from_user_mode - Architecture specific sanity check for user mode regs46 * arch_enter_from_user_mode - Architecture specific sanity check for user mode regs
38 * @regs: Pointer to currents pt_regs47 * @regs: Pointer to currents pt_regs
@@ -203,6 +212,7 @@ unsigned long exit_to_user_mode_loop(struct pt_regs *regs, unsigned long ti_work · 164 unmodified lines
203/**212/**
204 * __exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required213 * __exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required
205 * @regs: Pointer to pt_regs on entry stack214 * @regs: Pointer to pt_regs on entry stack
215 * @work_mask: Which TIF bits need to be evaluated
206 *216 *
207 * 1) check that interrupts are disabled217 * 1) check that interrupts are disabled
208 * 2) call tick_nohz_user_enter_prepare()218 * 2) call tick_nohz_user_enter_prepare()
@@ -212,7 +222,8 @@ unsigned long exit_to_user_mode_loop(struct pt_regs *regs, unsigned long ti_work · 3 unmodified lines
212 *222 *
213 * Don't invoke directly, use the syscall/irqentry_ prefixed variants below223 * Don't invoke directly, use the syscall/irqentry_ prefixed variants below
214 */224 */
215static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs)225static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs,
226 const unsigned long work_mask)
216{227{
217 unsigned long ti_work;228 unsigned long ti_work;
218 229
@@ -222,8 +233,10 @@ static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs) · 3 unmodified lines
222 tick_nohz_user_enter_prepare();233 tick_nohz_user_enter_prepare();
223 234
224 ti_work = read_thread_flags();235 ti_work = read_thread_flags();
225 if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK))236 if (unlikely(ti_work & work_mask)) {
226 ti_work = exit_to_user_mode_loop(regs, ti_work);237 if (!hrtimer_rearm_deferred_user_irq(&ti_work, work_mask))
238 ti_work = exit_to_user_mode_loop(regs, ti_work);
239 }
227 240
228 arch_exit_to_user_mode_prepare(regs, ti_work);241 arch_exit_to_user_mode_prepare(regs, ti_work);
229}242}
@@ -239,7 +252,7 @@ static __always_inline void __exit_to_user_mode_validate(void) · 9 unmodified lines
239/* Temporary workaround to keep ARM64 alive */252/* Temporary workaround to keep ARM64 alive */
240static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)253static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)
241{254{
242 __exit_to_user_mode_prepare(regs);255 __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK);
243 rseq_exit_to_user_mode_legacy();256 rseq_exit_to_user_mode_legacy();
244 __exit_to_user_mode_validate();257 __exit_to_user_mode_validate();
245}258}
@@ -253,7 +266,7 @@ static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *reg · 7 unmodified lines
253 */266 */
254static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *regs)267static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *regs)
255{268{
256 __exit_to_user_mode_prepare(regs);269 __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK_SYSCALL);
257 rseq_syscall_exit_to_user_mode();270 rseq_syscall_exit_to_user_mode();
258 __exit_to_user_mode_validate();271 __exit_to_user_mode_validate();
259}272}
@@ -267,7 +280,7 @@ static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *re · 7 unmodified lines
267 */280 */
268static __always_inline void irqentry_exit_to_user_mode_prepare(struct pt_regs *regs)281static __always_inline void irqentry_exit_to_user_mode_prepare(struct pt_regs *regs)
269{282{
270 __exit_to_user_mode_prepare(regs);283 __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK_IRQ);
271 rseq_irqentry_exit_to_user_mode();284 rseq_irqentry_exit_to_user_mode();
272 __exit_to_user_mode_validate();285 __exit_to_user_mode_validate();
273}286}
include/linux/rseq_entry.h+13−3
@@ -40,6 +40,7 @@ DECLARE_PER_CPU(struct rseq_stats, rseq_stats); · 39 unmodified lines
40#endif /* !CONFIG_RSEQ_STATS */40#endif /* !CONFIG_RSEQ_STATS */
41 41
42#ifdef CONFIG_RSEQ42#ifdef CONFIG_RSEQ
43#include <linux/hrtimer_rearm.h>
43#include <linux/jump_label.h>44#include <linux/jump_label.h>
44#include <linux/rseq.h>45#include <linux/rseq.h>
45#include <linux/sched/signal.h>46#include <linux/sched/signal.h>
@@ -110,7 +111,7 @@ static __always_inline void rseq_slice_clear_grant(struct task_struct *t) · 64 unmodified lines
110 t->rseq.slice.state.granted = false;111 t->rseq.slice.state.granted = false;
111}112}
112 113
113static __always_inline bool rseq_grant_slice_extension(bool work_pending)114static __always_inline bool __rseq_grant_slice_extension(bool work_pending)
114{115{
115 struct task_struct *curr = current;116 struct task_struct *curr = current;
116 struct rseq_slice_ctrl usr_ctrl;117 struct rseq_slice_ctrl usr_ctrl;
@@ -215,11 +216,20 @@ efault: · 98 unmodified lines
215 return false;216 return false;
216}217}
217 218
219static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask)
220{
221 if (unlikely(__rseq_grant_slice_extension(ti_work & mask))) {
222 hrtimer_rearm_deferred_tif(ti_work);
223 return true;
224 }
225 return false;
226}
227
218#else /* CONFIG_RSEQ_SLICE_EXTENSION */228#else /* CONFIG_RSEQ_SLICE_EXTENSION */
219static inline bool rseq_slice_extension_enabled(void) { return false; }229static inline bool rseq_slice_extension_enabled(void) { return false; }
220static inline bool rseq_arm_slice_extension_timer(void) { return false; }230static inline bool rseq_arm_slice_extension_timer(void) { return false; }
221static inline void rseq_slice_clear_grant(struct task_struct *t) { }231static inline void rseq_slice_clear_grant(struct task_struct *t) { }
222static inline bool rseq_grant_slice_extension(bool work_pending) { return false; }232static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
223#endif /* !CONFIG_RSEQ_SLICE_EXTENSION */233#endif /* !CONFIG_RSEQ_SLICE_EXTENSION */
224 234
225bool rseq_debug_update_user_cs(struct task_struct *t, struct pt_regs *regs, unsigned long csaddr);235bool rseq_debug_update_user_cs(struct task_struct *t, struct pt_regs *regs, unsigned long csaddr);
@@ -778,7 +788,7 @@ static inline void rseq_syscall_exit_to_user_mode(void) { } · 552 unmodified lines
778static inline void rseq_irqentry_exit_to_user_mode(void) { }788static inline void rseq_irqentry_exit_to_user_mode(void) { }
779static inline void rseq_exit_to_user_mode_legacy(void) { }789static inline void rseq_exit_to_user_mode_legacy(void) { }
780static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }790static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
781static inline bool rseq_grant_slice_extension(bool work_pending) { return false; }791static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
782#endif /* !CONFIG_RSEQ */792#endif /* !CONFIG_RSEQ */
783 793
784#endif /* _LINUX_RSEQ_ENTRY_H */794#endif /* _LINUX_RSEQ_ENTRY_H */
kernel/entry/common.c+3−1
@@ -50,7 +50,7 @@ static __always_inline unsigned long __exit_to_user_mode_loop(struct pt_regs *re · 49 unmodified lines
50 local_irq_enable_exit_to_user(ti_work);50 local_irq_enable_exit_to_user(ti_work);
51 51
52 if (ti_work & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) {52 if (ti_work & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) {
53 if (!rseq_grant_slice_extension(ti_work & TIF_SLICE_EXT_DENY))53 if (!rseq_grant_slice_extension(ti_work, TIF_SLICE_EXT_DENY))
54 schedule();54 schedule();
55 }55 }
56 56
@@ -225,6 +225,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state) · 168 unmodified lines
225 */225 */
226 if (state.exit_rcu) {226 if (state.exit_rcu) {
227 instrumentation_begin();227 instrumentation_begin();
228 hrtimer_rearm_deferred();
228 /* Tell the tracer that IRET will enable interrupts */229 /* Tell the tracer that IRET will enable interrupts */
229 trace_hardirqs_on_prepare();230 trace_hardirqs_on_prepare();
230 lockdep_hardirqs_on_prepare();231 lockdep_hardirqs_on_prepare();
@@ -238,6 +239,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state) · 7 unmodified lines
238 if (IS_ENABLED(CONFIG_PREEMPTION))239 if (IS_ENABLED(CONFIG_PREEMPTION))
239 irqentry_exit_cond_resched();240 irqentry_exit_cond_resched();
240 241
242 hrtimer_rearm_deferred();
241 /* Covers both tracing and lockdep */243 /* Covers both tracing and lockdep */
242 trace_hardirqs_on();244 trace_hardirqs_on();
243 instrumentation_end();245 instrumentation_end();

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t