Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sched/clock: Provide local_clock_noinstr()

Now that all ARCH_WANTS_NO_INSTR architectures (arm64, loongarch,
s390, x86) provide sched_clock_noinstr(), use this to provide
local_clock_noinstr().

This local_clock_noinstr() will be safe to use from noinstr code with
the assumption that any such noinstr code is non-preemptible (it had
better be, entry code will have IRQs disabled while __cpuidle must
have preemption disabled).

Specifically, preempt_enable_notrace(), a common part of many a
sched_clock() implementation calls out to schedule() -- even though,
per the above, it will never trigger -- which frustrates noinstr
validation.

vmlinux.o: warning: objtool: local_clock+0xb5: call to preempt_schedule_notrace_thunk() leaves .noinstr.text section

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Michael Kelley <mikelley@microsoft.com> # Hyper-V
Link: https://lore.kernel.org/r/20230519102715.978624636@infradead.org

+29 -7
+16 -1
include/linux/sched/clock.h
··· 12 12 * 13 13 * Please use one of the three interfaces below. 14 14 */ 15 - extern unsigned long long notrace sched_clock(void); 15 + extern u64 sched_clock(void); 16 + 17 + #if defined(CONFIG_ARCH_WANTS_NO_INSTR) || defined(CONFIG_GENERIC_SCHED_CLOCK) 18 + extern u64 sched_clock_noinstr(void); 19 + #else 20 + static __always_inline u64 sched_clock_noinstr(void) 21 + { 22 + return sched_clock(); 23 + } 24 + #endif 16 25 17 26 /* 18 27 * See the comment in kernel/sched/clock.c ··· 52 43 static inline u64 cpu_clock(int cpu) 53 44 { 54 45 return sched_clock(); 46 + } 47 + 48 + static __always_inline u64 local_clock_noinstr(void) 49 + { 50 + return sched_clock_noinstr(); 55 51 } 56 52 57 53 static __always_inline u64 local_clock(void) ··· 93 79 return sched_clock_cpu(cpu); 94 80 } 95 81 82 + extern u64 local_clock_noinstr(void); 96 83 extern u64 local_clock(void); 97 84 98 85 #endif
+13 -6
kernel/sched/clock.c
··· 266 266 s64 delta; 267 267 268 268 again: 269 - now = sched_clock(); 269 + now = sched_clock_noinstr(); 270 270 delta = now - scd->tick_raw; 271 271 if (unlikely(delta < 0)) 272 272 delta = 0; ··· 293 293 return clock; 294 294 } 295 295 296 - noinstr u64 local_clock(void) 296 + noinstr u64 local_clock_noinstr(void) 297 297 { 298 298 u64 clock; 299 299 300 300 if (static_branch_likely(&__sched_clock_stable)) 301 - return sched_clock() + __sched_clock_offset; 301 + return sched_clock_noinstr() + __sched_clock_offset; 302 302 303 303 if (!static_branch_likely(&sched_clock_running)) 304 - return sched_clock(); 304 + return sched_clock_noinstr(); 305 305 306 - preempt_disable_notrace(); 307 306 clock = sched_clock_local(this_scd()); 308 - preempt_enable_notrace(); 309 307 310 308 return clock; 309 + } 310 + 311 + u64 local_clock(void) 312 + { 313 + u64 now; 314 + preempt_disable_notrace(); 315 + now = local_clock_noinstr(); 316 + preempt_enable_notrace(); 317 + return now; 311 318 } 312 319 EXPORT_SYMBOL_GPL(local_clock); 313 320