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
33 #define __LINUX_IRQENTRYCOMMON_H
44
55 #include <linux/context_tracking.h>
6+#include <linux/hrtimer_rearm.h>
67 #include <linux/kmsan.h>
78 #include <linux/rseq_entry.h>
89 #include <linux/static_call_types.h>
@@ -33,6 +34,14 @@ · 24 unmodified lines
3334 _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | _TIF_RSEQ | \
3435 ARCH_EXIT_TO_USER_MODE_WORK)
3536
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+
3645 /**
3746 * arch_enter_from_user_mode - Architecture specific sanity check for user mode regs
3847 * @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
203212 /**
204213 * __exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required
205214 * @regs: Pointer to pt_regs on entry stack
215+ * @work_mask: Which TIF bits need to be evaluated
206216 *
207217 * 1) check that interrupts are disabled
208218 * 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
212222 *
213223 * Don't invoke directly, use the syscall/irqentry_ prefixed variants below
214224 */
215−static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs)
225+static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs,
226+ const unsigned long work_mask)
216227 {
217228 unsigned long ti_work;
218229
@@ -222,8 +233,10 @@ static __always_inline void __exit_to_user_mode_prepare(struct pt_regs *regs) · 3 unmodified lines
222233 tick_nohz_user_enter_prepare();
223234
224235 ti_work = read_thread_flags();
225− if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK))
226− ti_work = exit_to_user_mode_loop(regs, ti_work);
236+ if (unlikely(ti_work & work_mask)) {
237+ if (!hrtimer_rearm_deferred_user_irq(&ti_work, work_mask))
238+ ti_work = exit_to_user_mode_loop(regs, ti_work);
239+ }
227240
228241 arch_exit_to_user_mode_prepare(regs, ti_work);
229242 }
@@ -239,7 +252,7 @@ static __always_inline void __exit_to_user_mode_validate(void) · 9 unmodified lines
239252 /* Temporary workaround to keep ARM64 alive */
240253 static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)
241254 {
242− __exit_to_user_mode_prepare(regs);
255+ __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK);
243256 rseq_exit_to_user_mode_legacy();
244257 __exit_to_user_mode_validate();
245258 }
@@ -253,7 +266,7 @@ static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *reg · 7 unmodified lines
253266 */
254267 static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *regs)
255268 {
256− __exit_to_user_mode_prepare(regs);
269+ __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK_SYSCALL);
257270 rseq_syscall_exit_to_user_mode();
258271 __exit_to_user_mode_validate();
259272 }
@@ -267,7 +280,7 @@ static __always_inline void syscall_exit_to_user_mode_prepare(struct pt_regs *re · 7 unmodified lines
267280 */
268281 static __always_inline void irqentry_exit_to_user_mode_prepare(struct pt_regs *regs)
269282 {
270− __exit_to_user_mode_prepare(regs);
283+ __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK_IRQ);
271284 rseq_irqentry_exit_to_user_mode();
272285 __exit_to_user_mode_validate();
273286 }
include/linux/rseq_entry.h+13−3
@@ -40,6 +40,7 @@ DECLARE_PER_CPU(struct rseq_stats, rseq_stats); · 39 unmodified lines
4040 #endif /* !CONFIG_RSEQ_STATS */
4141
4242 #ifdef CONFIG_RSEQ
43+#include <linux/hrtimer_rearm.h>
4344 #include <linux/jump_label.h>
4445 #include <linux/rseq.h>
4546 #include <linux/sched/signal.h>
@@ -110,7 +111,7 @@ static __always_inline void rseq_slice_clear_grant(struct task_struct *t) · 64 unmodified lines
110111 t->rseq.slice.state.granted = false;
111112 }
112113
113−static __always_inline bool rseq_grant_slice_extension(bool work_pending)
114+static __always_inline bool __rseq_grant_slice_extension(bool work_pending)
114115 {
115116 struct task_struct *curr = current;
116117 struct rseq_slice_ctrl usr_ctrl;
@@ -215,11 +216,20 @@ efault: · 98 unmodified lines
215216 return false;
216217 }
217218
219+static __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+
218228 #else /* CONFIG_RSEQ_SLICE_EXTENSION */
219229 static inline bool rseq_slice_extension_enabled(void) { return false; }
220230 static inline bool rseq_arm_slice_extension_timer(void) { return false; }
221231 static inline void rseq_slice_clear_grant(struct task_struct *t) { }
222−static inline bool rseq_grant_slice_extension(bool work_pending) { return false; }
232+static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
223233 #endif /* !CONFIG_RSEQ_SLICE_EXTENSION */
224234
225235 bool 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
778788 static inline void rseq_irqentry_exit_to_user_mode(void) { }
779789 static inline void rseq_exit_to_user_mode_legacy(void) { }
780790 static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
781−static inline bool rseq_grant_slice_extension(bool work_pending) { return false; }
791+static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
782792 #endif /* !CONFIG_RSEQ */
783793
784794 #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
5050 local_irq_enable_exit_to_user(ti_work);
5151
5252 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))
5454 schedule();
5555 }
5656
@@ -225,6 +225,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state) · 168 unmodified lines
225225 */
226226 if (state.exit_rcu) {
227227 instrumentation_begin();
228+ hrtimer_rearm_deferred();
228229 /* Tell the tracer that IRET will enable interrupts */
229230 trace_hardirqs_on_prepare();
230231 lockdep_hardirqs_on_prepare();
@@ -238,6 +239,7 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state) · 7 unmodified lines
238239 if (IS_ENABLED(CONFIG_PREEMPTION))
239240 irqentry_exit_cond_resched();
240241
242+ hrtimer_rearm_deferred();
241243 /* Covers both tracing and lockdep */
242244 trace_hardirqs_on();
243245 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