NOHZ updates for v4.6
Subsystems (7)
Ingo Molnar
maintainerLinus,
Please pull the latest timers-nohz-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers-nohz-for-linus
# HEAD: 1f25184656a00a59e3a953189070d42a749f6aee Merge branch 'timers/core-v9' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks into timers/nohz
Please pull NOHZ enhancements, by Frederic Weisbecker, which reorganizes/refactors the NOHZ 'can the tick be stopped?' infrastructure and related code to be data driven, and harmonizes the naming and handling of all the various properties.
Querying it all is now a matter of:
triton:~/tip> git grep TICK_DEP_ kernel/
kernel/events/core.c: tick_dep_clear_cpu(smp_processor_id(), TICK_DEP_BIT_PERF_EVENTS);
kernel/events/core.c: tick_nohz_dep_clear(TICK_DEP_BIT_PERF_EVENTS);
kernel/events/core.c: tick_dep_set_cpu(smp_processor_id(), TICK_DEP_BIT_PERF_EVENTS);
kernel/events/core.c: tick_nohz_dep_set(TICK_DEP_BIT_PERF_EVENTS);
kernel/sched/clock.c: tick_dep_clear(TICK_DEP_BIT_CLOCK_UNSTABLE);
kernel/sched/clock.c: tick_dep_set(TICK_DEP_BIT_CLOCK_UNSTABLE);
kernel/sched/sched.h: tick_nohz_dep_clear_cpu(cpu, TICK_DEP_BIT_SCHED);
kernel/sched/sched.h: tick_nohz_dep_set_cpu(cpu, TICK_DEP_BIT_SCHED);
kernel/time/posix-cpu-timers.c: tick_dep_set_task(p, TICK_DEP_BIT_POSIX_TIMER);
kernel/time/posix-cpu-timers.c: tick_dep_set_signal(p->signal, TICK_DEP_BIT_POSIX_TIMER);
kernel/time/posix-cpu-timers.c: tick_dep_clear_task(tsk, TICK_DEP_BIT_POSIX_TIMER);
kernel/time/posix-cpu-timers.c: tick_dep_clear_signal(sig, TICK_DEP_BIT_POSIX_TIMER);
kernel/time/posix-cpu-timers.c: tick_dep_set_signal(tsk->signal, TICK_DEP_BIT_POSIX_TIMER);
kernel/time/tick-sched.c: if (dep & TICK_DEP_MASK_POSIX_TIMER) {
kernel/time/tick-sched.c: trace_tick_stop(0, TICK_DEP_MASK_POSIX_TIMER);
kernel/time/tick-sched.c: if (dep & TICK_DEP_MASK_PERF_EVENTS) {
kernel/time/tick-sched.c: trace_tick_stop(0, TICK_DEP_MASK_PERF_EVENTS);
kernel/time/tick-sched.c: if (dep & TICK_DEP_MASK_SCHED) {
kernel/time/tick-sched.c: trace_tick_stop(0, TICK_DEP_MASK_SCHED);
kernel/time/tick-sched.c: if (dep & TICK_DEP_MASK_CLOCK_UNSTABLE)
kernel/time/tick-sched.c: trace_tick_stop(0, TICK_DEP_MASK_CLOCK_UNSTABLE);
kernel/time/tick-sched.c: trace_tick_stop(1, TICK_DEP_MASK_NONE);Thanks,
Ingo
------------------>
Frederic Weisbecker (9):
atomic: Export fetch_or()
nohz: Implement wide kick on top of irq work
nohz: New tick dependency mask
nohz: Use enum code for tick stop failure tracing message
perf: Migrate perf to use new tick dependency mask model
sched: Account rr tasks
sched: Migrate sched to use new tick dependency mask model
posix-cpu-timers: Migrate to use new tick dependency mask model
sched-clock: Migrate to use new tick dependency mask model
include/linux/atomic.h | 21 +++++ include/linux/perf_event.h | 6 -- include/linux/posix-timers.h | 3 - include/linux/sched.h | 11 ++- include/linux/tick.h | 97 ++++++++++++++++++++++- include/trace/events/timer.h | 36 +++++++-- kernel/events/core.c | 65 +++++++++++---- kernel/sched/clock.c | 5 ++ kernel/sched/core.c | 49 +++++------- kernel/sched/rt.c | 16 ++++ kernel/sched/sched.h | 48 +++++++---- kernel/time/posix-cpu-timers.c | 52 +++--------- kernel/time/tick-sched.c | 175 ++++++++++++++++++++++++++++++++--------- kernel/time/tick-sched.h | 1 + 14 files changed, 424 insertions(+), 161 deletions(-)
include/linux/atomic.h +21 -0
@@ -548,6 +548,27 @@ static inline int atomic_dec_if_positive(atomic_t *v) }#endif +/** +* fetch_or - perform *ptr |= mask and return old value of *ptr +* @ptr: pointer to value +* @mask: mask to OR on the value +* +* cmpxchg based fetch_or, macro so it works for different integer types +*/ +#ifndef fetch_or +#define fetch_or (ptr, mask) \ +({ typeof(*(ptr)) __old, __val = *(ptr); \ + for (;;) { \ + __old = cmpxchg((ptr), __val, __val | (mask)); \ + if (__old == __val) \ + break; \ + __val = __old; \ + } \ + __old; \ +}) +#endif + +#ifdef CONFIG_GENERIC_ATOMIC64 #include <asm-generic/atomic64.h> #endif
include/linux/perf_event.h +0 -6
@@ -1109,12 +1109,6 @@ static inline void perf_event_task_tick(void) { }static inline int perf_event_release_kernel (struct perf_event *event) {return 0 ; }#endif -#if defined(CONFIG_PERF_EVENTS ) && defined(CONFIG_NO_HZ_FULL ) -extern bool perf_event_can_stop_tick (void ); -#else -static inline bool perf_event_can_stop_tick (void ) {return true; } -#endif -#if defined(CONFIG_PERF_EVENTS ) && defined(CONFIG_CPU_SUP_INTEL )extern void perf_restore_debug_store (void );#else
include/linux/posix-timers.h +0 -3
@@ -128,9 +128,6 @@ void posix_cpu_timer_schedule(struct k_itimer *timer);void run_posix_cpu_timers (struct task_struct *task);void posix_cpu_timers_exit (struct task_struct *task);void posix_cpu_timers_exit_group (struct task_struct *task); - -bool posix_cpu_timers_can_stop_tick (struct task_struct *tsk); -void set_process_cpu_timer (struct task_struct *task,unsigned int clock_idx,cputime_t *newval,cputime_t *oldval);
include/linux/sched.h +8 -3
@@ -719,6 +719,10 @@ struct signal_struct {/* Earliest-expiration cache. */ struct task_cputime cputime_expires; +#ifdef CONFIG_NO_HZ_FULL +unsigned long tick_dep_mask; +#endif +struct list_head cpu_timers[3 ];struct pid *tty_old_pgrp;
@@ -1542,6 +1546,10 @@ struct task_struct {VTIME_SYS , } vtime_snap_whence;#endif + +#ifdef CONFIG_NO_HZ_FULL +unsigned long tick_dep_mask; +#endif unsigned long nvcsw, nivcsw;/* context switch counts */ u64 start_time;/* monotonic time in nsec */ u64 real_start_time;/* boot based time in nsec */
@@ -2356,10 +2364,7 @@ static inline void wake_up_nohz_cpu(int cpu) { }#endif #ifdef CONFIG_NO_HZ_FULL -extern bool sched_can_stop_tick (void );extern u64 scheduler_tick_max_deferment (void ); -#else -static inline bool sched_can_stop_tick (void ) {return false; }#endif #ifdef CONFIG_SCHED_AUTOGROUP
include/linux/tick.h +93 -4
117 lines not on this page · raw message
include/trace/events/timer.h +31 -5
@@ -328,23 +328,49 @@ TRACE_EVENT(itimer_expire, );#ifdef CONFIG_NO_HZ_COMMON + +#define TICK_DEP_NAMES \ + tick_dep_name(NONE) \ + tick_dep_name(POSIX_TIMER) \ + tick_dep_name(PERF_EVENTS) \ + tick_dep_name(SCHED) \ + tick_dep_name_end(CLOCK_UNSTABLE) + +#undef tick_dep_name +#undef tick_dep_name_end + +#define tick_dep_name (sdep) TRACE_DEFINE_ENUM(TICK_DEP_MASK_##sdep); +#define tick_dep_name_end (sdep) TRACE_DEFINE_ENUM(TICK_DEP_MASK_##sdep); + +TICK_DEP_NAMES + +#undef tick_dep_name +#undef tick_dep_name_end + +#define tick_dep_name (sdep) { TICK_DEP_MASK_##sdep, #sdep }, +#define tick_dep_name_end (sdep) { TICK_DEP_MASK_##sdep, #sdep } + +#define show_tick_dep_name (val) \ + __print_symbolic(val, TICK_DEP_NAMES) +TRACE_EVENT (tick_stop, - TP_PROTO(int success, char *error_msg), + TP_PROTO(int success, int dependency), -TP_ARGS (success, error_msg), +TP_ARGS (success, dependency),TP_STRUCT__entry ( __field( int , success ) -__string ( msg, error_msg ) +__field ( int , dependency ) ),TP_fast_assign ( __entry->success = success; - __assign_str(msg, error_msg); + __entry->dependency = dependency; ), -TP_printk ("success=%s msg=%s" , __entry->success ?"yes" :"no" ,__get_str (msg)) +TP_printk ("success=%d dependency=%s" , __entry->success, \ +show_tick_dep_name (__entry->dependency)) );#endif
kernel/events/core.c +48 -17
109 lines not on this page · raw message
kernel/sched/clock.c +5 -0
@@ -61,6 +61,7 @@#include <linux/static_key.h> #include <linux/workqueue.h> #include <linux/compiler.h> +#include <linux/tick.h> /* * Scheduler clock - returns current time in nanosec units.
@@ -89,6 +90,8 @@ static void __set_sched_clock_stable(void) {if (!sched_clock_stable ())static_key_slow_inc (&__sched_clock_stable); + +tick_dep_clear (TICK_DEP_BIT_CLOCK_UNSTABLE ); }void set_sched_clock_stable (void )
@@ -108,6 +111,8 @@ static void __clear_sched_clock_stable(struct work_struct *work)/* XXX worry about clock continuity */ if (sched_clock_stable ())static_key_slow_dec (&__sched_clock_stable); + +tick_dep_set (TICK_DEP_BIT_CLOCK_UNSTABLE ); } static DECLARE_WORK(sched_clock_work, __clear_sched_clock_stable);
kernel/sched/core.c +20 -29
71 lines not on this page · raw message
kernel/sched/rt.c +16 -0
@@ -1142,12 +1142,27 @@ unsigned int rt_se_nr_running(struct sched_rt_entity *rt_se) }static inline +unsigned int rt_se_rr_nr_running (struct sched_rt_entity *rt_se) +{ +struct rt_rq *group_rq =group_rt_rq (rt_se); +struct task_struct *tsk; + +if (group_rq) +return group_rq->rr_nr_running; + + tsk =rt_task_of (rt_se); + +return (tsk->policy ==SCHED_RR ) ?1 :0 ; +} + +static inline void inc_rt_tasks (struct sched_rt_entity *rt_se,struct rt_rq *rt_rq) {int prio =rt_se_prio (rt_se);WARN_ON (!rt_prio (prio)); rt_rq->rt_nr_running +=rt_se_nr_running (rt_se); + rt_rq->rr_nr_running +=rt_se_rr_nr_running (rt_se);inc_rt_prio (rt_rq, prio);inc_rt_migration (rt_se, rt_rq);
@@ -1160,6 +1175,7 @@ void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)WARN_ON (!rt_prio (rt_se_prio (rt_se)));WARN_ON (!rt_rq->rt_nr_running); rt_rq->rt_nr_running -=rt_se_nr_running (rt_se); + rt_rq->rr_nr_running -=rt_se_rr_nr_running (rt_se);dec_rt_prio (rt_rq,rt_se_prio (rt_se));dec_rt_migration (rt_se, rt_rq);
kernel/sched/sched.h +34 -14
72 lines not on this page · raw message
kernel/time/posix-cpu-timers.c +11 -41
106 lines not on this page · raw message
kernel/time/tick-sched.c +136 -39
260 lines not on this page · raw message
kernel/time/tick-sched.h +1 -0
@@ -60,6 +60,7 @@ struct tick_sched {u64 next_timer;ktime_t idle_expires;int do_timer_last; +unsigned long tick_dep_mask; };extern struct tick_sched *tick_get_tick_sched (int cpu);
Quoted 9 lines · Ingo Molnar
include/linux/atomic.h +9
-0+/** +* fetch_or - perform *ptr |= mask and return old value of *ptr +* @ptr: pointer to value +* @mask: mask to OR on the value +* +* cmpxchg based fetch_or, macro so it works for different integer types +*/ +#ifndef fetch_or +#define fetch_or (ptr, mask) \
include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 569
Quoted 10 lines
include/linux/atomic.h +10
-0+({ typeof(*(ptr)) __old, __val = *(ptr); \ +for (;;) { \ + __old =cmpxchg ((ptr), __val, __val | (mask)); \ +if (__old == __val) \ +break ; \ + __val = __old; \ + } \ + __old; \ +}) +#endif
This is garbage.
This macro re-uses the "mask" argument potentially many many times, so semantically it's very dubious.
That may have been acceptable in the old situation when this was internal to sched.c, but now the code was moved to a generic header file. And this kind of broken macro is *not* acceptable in that context any more.
It is now in asm-generic/atomic.h, so it should now conform to the rest of the code there. Try to model it around ATOMIC_OP_RETURN(), although obviously this fetch_or() function returns the value *before* the logical 'or' rather than the end result.
It would be lovely if it were piossible to just use an "atomic_t" type, but it looks like it is used on thread_info->flags. Which doesn't have a good type, sadly.
As a result, the code then makes a big deal about how this works with any integer type, but then the new code that uses it uses a stupid type that isn't appropriate. Why would it be using an "unsigned long", when
- it holds a fixed number of bits that don't depend on architecture
and certainly is not 64 (or even close to 32).
- the structure it is in was just preceded by an "int", so on 64-bit
it generates pointless padding in addition to the pointless 64-bit field.
The only reason to use a "unsigned long" is because "fetch_or()" would be hardcoded to that type, which doesn't seem to be true.
Now, in practice, the code looks like it should *work* fine, so I'm going to pull this, but I do want to lodge a protest on sloppiness and general and unnecessary uglicity of this code.
So please get this fixed.
Linus
Peter Zijlstra
maintainerQuoted 23 lines · Linus Torvalds
Quoted 9 lines · Ingo Molnar
(quoted diff) +9
-0+/** + * fetch_or - perform *ptr |= mask and return old value of *ptr + * @ptr: pointer to value + * @mask: mask to OR on the value + * + * cmpxchg based fetch_or, macro so it works for different integer types + */ +#ifndef fetch_or +#define fetch_or(ptr, mask) \Review
Quoted 10 lines
(quoted diff) +10
-0+({ typeof(*(ptr)) __old, __val = *(ptr); \ + for (;;) { \ + __old = cmpxchg((ptr), __val, __val | (mask)); \ + if (__old == __val) \ + break; \ + __val = __old; \ + } \ + __old; \ +}) +#endifThis is garbage.
Quoted 2 lines
This macro re-uses the "mask" argument potentially many many times, so semantically it's very dubious.
So the below cures that; but do we want to maybe pull this back into sched.c and expose a version that operates on a fixed type instead?
Although with xchg() and cmpxchg() we've already set a precedence for multi-width operators.
The whole thread_info::flags thing is rather 'special' and I'm not sure we even want to go clean that up, I can see why 64bit arch would very much want a 64bit flags word etc. And TIF_flags are very arch specific.
--- include/linux/atomic.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
include/linux/atomic.h +2 -1
@@ -558,8 +558,9 @@ static inline int atomic_dec_if_positive(atomic_t *v)#ifndef fetch_or#define fetch_or (ptr, mask) \ ({ typeof(*(ptr)) __old, __val = *(ptr); \ + typeof(mask) __mask = (mask); \ for (;;) { \ - __old = cmpxchg((ptr), __val, __val | (mask)); \ + __old = cmpxchg((ptr), __val, __val | __mask); \ if (__old == __val) \ break; \ __val = __old; \
Ingo Molnar
maintainerPATCH 1/1 atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Linus TorvaldsQuoted 14 lines · Linus Torvalds
Quoted 13 lines · Ingo Molnar
(quoted diff) +13
-0+/** + * fetch_or - perform *ptr |= mask and return old value of *ptr + * @ptr: pointer to value + * @mask: mask to OR on the value + * + * cmpxchg based fetch_or, macro so it works for different integer types + */ +#ifndef fetch_or +#define fetch_or(ptr, mask) \ +({ typeof(*(ptr)) __old, __val = *(ptr); \ + for (;;) { \ + __old = cmpxchg((ptr), __val, __val | (mask)); \ + if (__old == __val) \
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+ break; \ + __val = __old; \ + } \ + __old; \ +}) +#endifThis is garbage.
Indeed.
Quoted 2 lines
This macro re-uses the "mask" argument potentially many many times, so semantically it's very dubious.
Yes.
Quoted 9 lines
That may have been acceptable in the old situation when this was internal to
sched.c, but now the code was moved to a generic header file. And this kind of broken macro is *not* acceptable in that context any more.It is now in
asm-generic/atomic.h, so it should now conform to the rest of the code there. Try to model it aroundATOMIC_OP_RETURN(), although obviously thisfetch_or()function returns the value *before* the logical 'or' rather than the end result.
Ok.
I can see two other problems with it as well:
1) 'ptr' may get evaluated multiple times as well, not just 'mask'.
2) its naming sucks. "fetch_or()" does not really signal that it's a fundamentally atomic operation, nor what API family it belongs to.
Given that it's in include/linux/atomic.h, and it has _almost_ xchg() semantics - how about we name it xchg_or() and model it after that? From the semantics POV it's really an unconditional xchg()-alike API, even though technically it's a cmpxchg() loop.
I've attached a (totally untested) patch that fixes the bugs and does the rename.
Quoted 3 lines
It would be lovely if it were piossible to just use an "
atomic_t" type, but it looks like it is used onthread_info->flags. Which doesn't have a good type, sadly.
Indeed, thread_info->flags is 'almost' but not quite 'unsigned long':
triton:~/tip> grep -E '\<flags\>.*;' $(git grep -l 'thread_info {' arch/) | grep -vE 'WARN|return'
arch/alpha/include/asm/thread_info.h: unsigned int flags; /* low level flags */ arch/arc/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/arm/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/arm64/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/avr32/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/blackfin/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/c6x/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/cris/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/frv/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/h8300/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/hexagon/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/ia64/include/asm/thread_info.h: __u32 flags; /* thread_info flags (see TIF_*) */ arch/m32r/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/m68k/include/asm/thread_info.h: unsigned long flags; arch/metag/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/microblaze/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/mips/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/mn10300/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/nios2/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/openrisc/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/parisc/include/asm/thread_info.h: unsigned long flags; /* thread_info flags (see TIF_*) */ arch/powerpc/include/asm/thread_info.h: unsigned long flags ____cacheline_aligned_in_smp; arch/s390/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/score/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/sh/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/sparc/include/asm/thread_info_32.h: unsigned long flags; /* low level flags */ arch/sparc/include/asm/thread_info_64.h: unsigned long flags; arch/tile/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/um/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/unicore32/include/asm/thread_info.h: unsigned long flags; /* low level flags */ arch/x86/include/asm/thread_info.h: __u32 flags; /* low level flags */ arch/xtensa/include/asm/thread_info.h: unsigned long flags; /* low level flags */
alpha, ia64 and x86 are the odd ones out with an int ti::flags.
Quoted 11 lines
As a result, the code then makes a big deal about how this works with any integer type, but then the new code that uses it uses a stupid type that isn't appropriate. Why would it be using an "unsigned long", when
- it holds a fixed number of bits that don't depend on architecture
and certainly is not 64 (or even close to 32).- the structure it is in was just preceded by an "int", so on 64-bit
it generates pointless padding in addition to the pointless 64-bit field.
Yes, its natural type would be 'unsigned int'.
Quoted 2 lines
The only reason to use a "unsigned long" is because "
fetch_or()" would be hardcoded to that type, which doesn't seem to be true.
Yes, fetch_or() uses cmpxchg() internally, and AFAICS cmpxchg() is guaranteed to work on two integer types:
- 32 bit
- the natural word type of the architecture. (32 or 64 bit)
Many architectures have wider support for cmpxchg(): 8, 16, 32 and 64 bits, but we cannot rely on that in a generic header.
So changing the dependency mask type to 'unsigned int' should be safe. I'll send another patch for that.
Quoted 5 lines
Now, in practice, the code looks like it should *work* fine, so I'm going to pull this, but I do want to lodge a protest on sloppiness and general and unnecessary uglicity of this code.
So please get this fixed.
Sorry about this, that was quite sloppy on several levels!
Thanks,
Ingo
===========================>
From: Ingo Molnar <[email protected]>
Date: Tue, 15 Mar 2016 10:19:10 +0100
Subject: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
Linus noticed a couple of problems with the fetch_or() implementation introduced by 5fd7a09cfb8c ("atomic: Export fetch_or()"):
- Sloppy macro implementation: 'mask' and 'ptr' is evaluated multiple times, which will break if arguments have side effects.
- Sloppy semantics: the naming and structure of the macro is ambigious, with no well-defined semantics. It's neither an atomic nor a cmpxchg() interface, but still it lives in include/linux/atomic.h.
Solve this by:
- Extracting the arguments into helper variables once, and never using the original arguments from that point on. This makes it pretty clear that there can be no unwanted macro evaluation side effects.
- Renaming fetch_or() to xchg_or(), recognizing that the semantics are xchg()-alike.
Also propagate the rename to the call sites.
--- include/linux/atomic.h | 20 +++++++++++++------- kernel/sched/core.c | 2 +- kernel/time/tick-sched.c | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-)
include/linux/atomic.h +13 -7
@@ -549,21 +549,27 @@ static inline int atomic_dec_if_positive(atomic_t *v)#endif /** -* fetch_or - perform *ptr |= mask and return old value of *ptr -* @ptr: pointer to value +* xchg_or - perform *ptr |= mask atomically and return old value of *ptr +* @ptr: pointer to value (cmpxchg() compatible integer pointer type) * @mask: mask to OR on the value * -* cmpxchg based fetch_or, macro so it works for different integer types +* cmpxchg() based, it's a macro so it works for different integer types. */ -#ifndef fetch_or -#define fetch_or (ptr, mask) \ -({ typeof(*(ptr)) __old, __val = *(ptr); \ +#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) __ptr = (ptr); \ + typeof(mask) __mask = (mask); \ + \ + typeof(*(__ptr)) __old, __val = *__ptr; \ + \ for (;;) { \ - __old = cmpxchg((ptr), __val, __val | (mask)); \ + __old = cmpxchg(__ptr, __val, __val | __mask); \ if (__old == __val) \ break; \ __val = __old; \ } \ + \ __old; \ })#endif
kernel/sched/core.c +1 -1
@@ -330,7 +330,7 @@ static inline void init_hrtick(void)static bool set_nr_and_not_polling (struct task_struct *p) {struct thread_info *ti =task_thread_info (p); -return !(fetch_or (&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); +return !(xchg_or (&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); }/*
kernel/time/tick-sched.c +2 -2
@@ -264,7 +264,7 @@ static void tick_nohz_dep_set_all(unsigned long *dep, {unsigned long prev; - prev =fetch_or (dep,BIT_MASK (bit)); + prev =xchg_or (dep,BIT_MASK (bit));if (!prev)tick_nohz_full_kick_all (); }
@@ -294,7 +294,7 @@ void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) ts =per_cpu_ptr (&tick_cpu_sched, cpu); - prev =fetch_or (&ts->tick_dep_mask,BIT_MASK (bit)); + prev =xchg_or (&ts->tick_dep_mask,BIT_MASK (bit));if (!prev) {preempt_disable ();/* Perf needs local kick that is NMI safe */
Ingo Molnar
maintainerQuoted 24 lines · Peter Zijlstra
Quoted 23 lines · Linus Torvalds
Quoted 9 lines · Ingo Molnar
(quoted diff) +9
-0+/** + * fetch_or - perform *ptr |= mask and return old value of *ptr + * @ptr: pointer to value + * @mask: mask to OR on the value + * + * cmpxchg based fetch_or, macro so it works for different integer types + */ +#ifndef fetch_or +#define fetch_or(ptr, mask) \Review
Quoted 10 lines
(quoted diff) +10
-0+({ typeof(*(ptr)) __old, __val = *(ptr); \ + for (;;) { \ + __old = cmpxchg((ptr), __val, __val | (mask)); \ + if (__old == __val) \ + break; \ + __val = __old; \ + } \ + __old; \ +}) +#endifThis is garbage.
Quoted 5 lines
Quoted 2 lines
This macro re-uses the "mask" argument potentially many many times, so semantically it's very dubious.
So the below cures that; but do we want to maybe pull this back into
sched.cand expose a version that operates on a fixed type instead?
So there are other problems with the macro - see the patch I just sent that tries to fix all of them.
Quoted 2 lines
Although with
xchg()andcmpxchg()we've already set a precedence for multi-width operators.
Yes, and if we name the new API 'xchg_or()' as I did it in my patch then it all nicely fits into the existing scheme.
Thanks,
Ingo
Ingo Molnar
maintainerPATCH 1/1 nohz: Change tick_dep_mask from 'unsigned long' to 'unsigned int'
In reply to Linus TorvaldsLinus noticed that the new tick_dep_mask types introduced in:
d027d45d8a17 ("nohz: New tick dependency mask")
... are sloppily defined as 'unsigned long' - which is wasteful to carry just 4 bits and which may also create suboptimal data types on 64-bit systems with word alignment padding holes in them.
Fix this by changing the type to the more natural 'unsigned int'.
(The xchg_or() API will work fine with 'unsigned int' as well.)
--- include/linux/sched.h | 4 ++-- kernel/time/tick-sched.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-)
include/linux/sched.h +2 -2
@@ -718,7 +718,7 @@ struct signal_struct {struct task_cputime cputime_expires;#ifdef CONFIG_NO_HZ_FULL -unsigned long tick_dep_mask; +unsigned int tick_dep_mask;#endif struct list_head cpu_timers[3 ];
@@ -1548,7 +1548,7 @@ struct task_struct {#endif #ifdef CONFIG_NO_HZ_FULL -unsigned long tick_dep_mask; +unsigned int tick_dep_mask;#endif unsigned long nvcsw, nivcsw;/* context switch counts */ u64 start_time;/* monotonic time in nsec */
kernel/time/tick-sched.c +5 -6
@@ -157,9 +157,9 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)cpumask_var_t tick_nohz_full_mask;cpumask_var_t housekeeping_mask;bool tick_nohz_full_running; -static unsigned long tick_dep_mask; +static unsigned int tick_dep_mask; -static void trace_tick_dependency (unsigned long dep) +static void trace_tick_dependency (unsigned int dep) {if (dep &TICK_DEP_MASK_POSIX_TIMER ) {trace_tick_stop (0 ,TICK_DEP_MASK_POSIX_TIMER );
@@ -259,10 +259,9 @@ static void tick_nohz_full_kick_all(void)preempt_enable (); } -static void tick_nohz_dep_set_all (unsigned long *dep, -enum tick_dep_bits bit) +static void tick_nohz_dep_set_all (unsigned int *dep,enum tick_dep_bits bit) { -unsigned long prev; +unsigned int prev; prev =xchg_or (dep,BIT_MASK (bit));if (!prev)
@@ -289,7 +288,7 @@ void tick_nohz_dep_clear(enum tick_dep_bits bit)*/ void tick_nohz_dep_set_cpu (int cpu,enum tick_dep_bits bit) { -unsigned long prev; +unsigned int prev;struct tick_sched *ts; ts =per_cpu_ptr (&tick_cpu_sched, cpu);
Peter Zijlstra
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 2 lines · Ingo Molnar
2) its naming sucks. "
fetch_or()" does not really signal that it's a fundamentally atomic operation, nor what API family it belongs to.
I disagree there, the fetch-$op naming is widely used for atomic operations that return the previous value. See for example the C/C++11 atomic ops.
I've even thought about reworking our entire atomic*_t bits to match. That is, introduce all the fetch_$op primitives, then convert all the $op_return ones over and finally remove all the $op_return ones.
I've not done so because we're all so very used to $op_return that I'm sure people (and this would very much include me) would curse me for changing this.
The reason for fetch_$op is that it also works for irreversible operations like or. With or_return you simply cannot tell what the previous state was (with add_return you can do a simple subtraction to revert to the prior state).
And yes, some people use the xchg-$op naming, but its less widely used (x86 asm being one). Other also use swap-$op.
In any case, I prefer the name as it was.
Peter Zijlstra
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 14 lines · Ingo Molnar
kernel/time/tick-sched.c +1
-1@@ -264,7 +264,7 @@ static void tick_nohz_dep_set_all(unsigned long *dep, {unsigned long prev; - prev =fetch_or (dep,BIT_MASK (bit)); + prev =xchg_or (dep,BIT_MASK (bit));if (!prev)tick_nohz_full_kick_all (); }
kernel/time/tick-sched.c void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) · line 300
Quoted 8 lines
kernel/time/tick-sched.c +1
-1@@ -294,7 +294,7 @@ void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) ts =per_cpu_ptr (&tick_cpu_sched, cpu); - prev =fetch_or (&ts->tick_dep_mask,BIT_MASK (bit)); + prev =xchg_or (&ts->tick_dep_mask,BIT_MASK (bit));if (!prev) {preempt_disable ();/* Perf needs local kick that is NMI safe */
Those should probably also use BIT(), BIT_MASK() is specifically for bitmasks, and should be combined with BIT_WORD().
Ingo Molnar
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 7 lines · Ingo Molnar
Subject: [PATCH] atomic: Fix bugs in '
fetch_or()' and rename it to 'xchg_or()'Linus noticed a couple of problems with the
fetch_or()implementation introduced by5fd7a09cfb8c("atomic: Exportfetch_or()"):- Sloppy macro implementation: 'mask' and 'ptr' is evaluated multiple times, which will break if arguments have side effects.
So this shiny new patch manages to crash the x86 kernel with a NULL pointer dereference:
[ 0.143027] BUG: unable to handle kernel NULL pointer dereference at (null) [ 0.144000] IP: [<ffffffff8107c64c>] resched_curr+0x3c/0xc0
GCC manages to turn this:
static bool set_nr_and_not_polling(struct task_struct *p)
{
struct thread_info *ti = task_thread_info(p);
return !(xchg_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG);
}and this:
Quoted 14 lines
/**
+ *xchg_or- perform *ptr |= mask atomically and return old value of *ptr
+ * @ptr: pointer to value (cmpxchg()compatible integer pointer type)
* @mask: mask to OR on the value
*
+ *cmpxchg()based, it's a macro so it works for different integer types. */include/linux/atomic.h +7
-0+#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) __ptr = (ptr); \ + typeof(mask) __mask = (mask); \ + \ + typeof(*(__ptr)) __old, __val = *__ptr; \
include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 574
Quoted 10 lines
include/linux/atomic.h +3
-0+ \for (;;) { \ + __old =cmpxchg (__ptr, __val, __val | __mask); \if (__old == __val) \break ; \ __val = __old; \ } \ + \ __old; \ })
into:
41c1: 89 c2 mov %eax,%edx
41c3: 89 d6 mov %edx,%esi
41c5: 31 c9 xor %ecx,%ecx
41c7: 89 d0 mov %edx,%eax
41c9: 83 ce 08 or $0x8,%esi
41cc: f0 0f b1 31 lock cmpxchg %esi,(%rcx)note the RCX zeroing via XOR...
The original, working sequence is:
41c4: 89 c2 mov %eax,%edx
41c6: 89 d6 mov %edx,%esi
41c8: 89 d0 mov %edx,%eax
41ca: 83 ce 08 or $0x8,%esi
41cd: f0 0f b1 31 lock cmpxchg %esi,(%rcx)The change that makes the difference is the 'ptr' part of:
include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 567
Quoted 1 line
include/linux/atomic.h +1
-0+ __old =cmpxchg (__ptr, __val, __val | __mask); \
This variant works:
Review
Quoted 1 line
(quoted diff) +1
-0+ __old = cmpxchg((ptr), __val, __val | __mask); \
After a lot of staring PeterZ realized that __ptr aliases with the x86 cmpxchg() macro-jungle's __ptr name!!
So if I do a s/__ptr/_ptr it all works...
But IMHO this really highlights a fundamental weakness of all this macro magic, it's all way too fragile.
Why don't we introduce a boring family of APIs:
cmpxchg_8() cmpxchg_16() cmpxchg_32() cmpxchg_64() xchg_or_32() xchg_or_64() ...
... with none of this pesky auto-typing property and none of the macro-inside-a-macro crap? We could do clean types and would write them all in proper C, not fragile CPP.
It's not like we migrate between the types all that frequently - and even if we do, it's trivial.
hm?
Thanks,
Ingo
Ingo Molnar
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Peter ZijlstraQuoted 7 lines · Peter Zijlstra
Quoted 2 lines · Ingo Molnar
2) its naming sucks. "
fetch_or()" does not really signal that it's a fundamentally atomic operation, nor what API family it belongs to.I disagree there, the fetch-$op naming is widely used for atomic operations that return the previous value. See for example the C/C++11 atomic ops.
The problem I see is that we don't really have the fetch_*() naming in the kernel right now, while we do have the xchg_*() naming. The latter is 'obviously' an atomic operation - while 'fetch' could be anything.
No strong opinion, but I think fetch_or() is not a particularly good name.
Thanks,
Ingo
Ingo Molnar
maintainerRe: [PATCH] nohz: Change tick_dep_mask from 'unsigned long' to 'unsigned int'
In reply to Ingo MolnarQuoted 25 lines · Ingo Molnar
Linus noticed that the new
tick_dep_masktypes introduced in:
d027d45d8a17("nohz: New tick dependency mask")... are sloppily defined as 'unsigned long' - which is wasteful to carry just 4 bits and which may also create suboptimal data types on 64-bit systems with word alignment padding holes in them.
Fix this by changing the type to the more natural 'unsigned int'.
(The
xchg_or()API will work fine with 'unsigned int' as well.)Reported-by Linus Torvalds <[email protected]>Cc Chris Metcalf <[email protected]>Cc Frederic Weisbecker <[email protected]>Cc Christoph Lameter <[email protected]>Cc Chris Metcalf <[email protected]>Cc Ingo Molnar <[email protected]>Cc Luiz Capitulino <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Rik van Riel <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Viresh Kumar <[email protected]>Signed-off-by Ingo Molnar <[email protected]>
Quoted 4 lines
--- include/linux/sched.h | 4 ++-- kernel/time/tick-sched.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-)
Hm, so the problem I did not consider is the following:
triton:~/tip> git grep tick_dep_mask kernel | grep _bit
kernel/time/tick-sched.c: clear_bit(bit, &tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &ts->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &tsk->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &sig->tick_dep_mask);
and the bitops natural type (and in fact the only supported bitops type) is 'unsigned long'.
So it's not that easy to change a bitmask over to unsigned int.
Suggestions?
Thanks,
Ingo
Ingo Molnar
maintainerPATCH v2 1/1 atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarLinus noticed a couple of problems with the fetch_or() implementation introduced by 5fd7a09cfb8c ("atomic: Export fetch_or()"):
- Sloppy macro implementation: 'mask' and 'ptr' is evaluated multiple times, which will break if arguments have side effects. Also, it uses double-underscore temporary variables - which is dangerous as low level asm ones are using those too and they may alias in unexpected ways.
- Sloppy semantics: the naming and structure of the macro is ambigious, with no well-defined semantics. It's neither an atomic nor a cmpxchg() interface, but still it lives in include/linux/atomic.h.
Solve this by:
- Extracting the arguments into helper variables once, and never using the original arguments from that point on. This makes it pretty clear that there can be no unwanted macro evaluation side effects.
- Using single-underscore temporary variables.
- Renaming fetch_or() to xchg_or(), recognizing that the semantics are xchg()-alike.
Also propagate the rename to the call sites.
--- include/linux/atomic.h | 26 ++++++++++++++++---------- kernel/sched/core.c | 2 +- kernel/time/tick-sched.c | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-)
include/linux/atomic.h +16 -10
@@ -549,22 +549,28 @@ static inline int atomic_dec_if_positive(atomic_t *v)#endif /** -* fetch_or - perform *ptr |= mask and return old value of *ptr -* @ptr: pointer to value +* xchg_or - perform *ptr |= mask atomically and return old value of *ptr +* @ptr: pointer to value (cmpxchg() compatible integer pointer type) * @mask: mask to OR on the value * -* cmpxchg based fetch_or, macro so it works for different integer types +* cmpxchg() based, it's a macro so it works for different integer types. */ -#ifndef fetch_or -#define fetch_or (ptr, mask) \ -({ typeof(*(ptr)) __old, __val = *(ptr); \ +#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) _ptr = (ptr); \ + typeof(mask) _mask = (mask); \ + \ + typeof(*_ptr) _old, _val = *_ptr; \ + \ for (;;) { \ - __old = cmpxchg((ptr), __val, __val | (mask)); \ - if (__old == __val) \ + _old = cmpxchg(_ptr, _val, _val | _mask); \ + if (_old == _val) \ break; \ - __val = __old; \ + _val = _old; \ } \ - __old; \ + \ + _old; \ })#endif
kernel/sched/core.c +1 -1
@@ -329,7 +329,7 @@ static inline void init_hrtick(void)static bool set_nr_and_not_polling (struct task_struct *p) {struct thread_info *ti =task_thread_info (p); -return !(fetch_or (&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); +return !(xchg_or (&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); }/*
kernel/time/tick-sched.c +2 -2
@@ -264,7 +264,7 @@ static void tick_nohz_dep_set_all(unsigned long *dep, {unsigned long prev; - prev =fetch_or (dep,BIT_MASK (bit)); + prev =xchg_or (dep,BIT_MASK (bit));if (!prev)tick_nohz_full_kick_all (); }
@@ -294,7 +294,7 @@ void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) ts =per_cpu_ptr (&tick_cpu_sched, cpu); - prev =fetch_or (&ts->tick_dep_mask,BIT_MASK (bit)); + prev =xchg_or (&ts->tick_dep_mask,BIT_MASK (bit));if (!prev) {preempt_disable ();/* Perf needs local kick that is NMI safe */
Peter Zijlstra
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 7 lines · Ingo Molnar
include/linux/atomic.h +7
-0+#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) __ptr = (ptr); \ + typeof(mask) __mask = (mask); \ + \ + typeof(*(__ptr)) __old, __val = *__ptr; \
include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 574
Quoted 10 lines
include/linux/atomic.h +3
-0+ \for (;;) { \ + __old =cmpxchg (__ptr, __val, __val | __mask); \if (__old == __val) \break ; \ __val = __old; \ } \ + \ __old; \ })
As reported by you this explodes, and it obvious from the generated asm why:
48e1: 89 c2 mov %eax,%edx
48e3: 41 89 d0 mov %edx,%r8d
48e6: 31 c9 xor %ecx,%ecx
48e8: 89 d0 mov %edx,%eax
48ea: 41 83 c8 08 or $0x8,%r8d
48ee: f0 44 0f b1 01 lock cmpxchg %r8d,(%rcx)
48f3: 39 c2 cmp %eax,%edx
48f5: 75 ea jne 48e1 <resched_curr+0x31>That's an unconditional NULL deref.
What happens is that __ptr from xchg_or() aliasses with __ptr from cmpxchg() and weird stuff happens.
If you do: s/__ptr/_ptr/ or similar on the xchg_or() code it all works again.
Ingo Molnar
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 14 lines · Ingo Molnar
But IMHO this really highlights a fundamental weakness of all this macro magic, it's all way too fragile.
Why don't we introduce a boring family of APIs:
cmpxchg_8() cmpxchg_16() cmpxchg_32() cmpxchg_64() xchg_or_32() xchg_or_64() ...
Quoted 8 lines
... with none of this pesky auto-typing property and none of the macro-inside-a-macro crap? We could do clean types and would write them all in proper C, not fragile CPP.
It's not like we migrate between the types all that frequently - and even if we do, it's trivial.
hm?
So if we are still on the same page at this point, we'd have to add a pointer variant too I suspect:
cmpxchg_ptr() xchg_ptr()
... whose bitness may differ between architectures(subarches), but it would still be a single variant per architecture, i.e. still with pretty clear type propagation and with a very clear notion of which architecture supports what.
It looks like a lot of work, but it's all low complexity work AFAICS that could be partly automated.
Thanks,
Ingo
Ingo Molnar
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
Quoted 32 lines · Ingo Molnar
Quoted 14 lines · Ingo Molnar
But IMHO this really highlights a fundamental weakness of all this macro magic, it's all way too fragile.
Why don't we introduce a boring family of APIs:
cmpxchg_8() cmpxchg_16() cmpxchg_32() cmpxchg_64() xchg_or_32() xchg_or_64() ...Quoted 8 lines
... with none of this pesky auto-typing property and none of the macro-inside-a-macro crap? We could do clean types and would write them all in proper C, not fragile CPP.
It's not like we migrate between the types all that frequently - and even if we do, it's trivial.
hm?
So if we are still on the same page at this point, we'd have to add a pointer variant too I suspect:
cmpxchg_ptr() xchg_ptr()
Quoted 6 lines
... whose bitness may differ between architectures(subarches), but it would still be a single variant per architecture, i.e. still with pretty clear type propagation and with a very clear notion of which architecture supports what.
It looks like a lot of work, but it's all low complexity work AFAICS that could be partly automated.
Btw., if we do all this, we could still add auto-type API variants, but now they would be implemented at the highest level, with none of the auto-type complexity pushed down to the architecture level. Architectures just provide their set of APIs for a given list of types, and that's it.
I hate to see all the auto-typing complexity pushed down to the arch assembly level:
/*
* Atomic compare and exchange. Compare OLD with MEM, if identical,
* store NEW in MEM. Return the initial value in MEM. Success is
* indicated by comparing RETURN with OLD.
*/
#define __raw_cmpxchg(ptr, old, new, size, lock) \
({ \
__typeof__(*(ptr)) __ret; \
__typeof__(*(ptr)) __old = (old); \
__typeof__(*(ptr)) __new = (new); \
switch (size) { \
case __X86_CASE_B: \
{ \
volatile u8 *__ptr = (volatile u8 *)(ptr); \
asm volatile(lock "cmpxchgb %2,%1" \
: "=a" (__ret), "+m" (*__ptr) \
: "q" (__new), "0" (__old) \
: "memory"); \
break; \
} \
case __X86_CASE_W: \
{ \
volatile u16 *__ptr = (volatile u16 *)(ptr); \
asm volatile(lock "cmpxchgw %2,%1" \
: "=a" (__ret), "+m" (*__ptr) \
: "r" (__new), "0" (__old) \
: "memory"); \
break; \
} \
case __X86_CASE_L: \
{ \
volatile u32 *__ptr = (volatile u32 *)(ptr); \
asm volatile(lock "cmpxchgl %2,%1" \
: "=a" (__ret), "+m" (*__ptr) \
: "r" (__new), "0" (__old) \
: "memory"); \
break; \
} \
case __X86_CASE_Q: \
{ \
volatile u64 *__ptr = (volatile u64 *)(ptr); \
asm volatile(lock "cmpxchgq %2,%1" \
: "=a" (__ret), "+m" (*__ptr) \
: "r" (__new), "0" (__old) \
: "memory"); \
break; \
} \
default: \
__cmpxchg_wrong_size(); \
} \
__ret; \
})it makes things harder to read, harder to debug and harder to optimize.
Thanks,
Ingo
Peter Zijlstra
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 6 lines · Ingo Molnar
Quoted 4 lines · Peter Zijlstra
Quoted 2 lines · Ingo Molnar
2) its naming sucks. "
fetch_or()" does not really signal that it's a fundamentally atomic operation, nor what API family it belongs to.
Quoted 7 lines
Quoted 3 lines
I disagree there, the fetch-$op naming is widely used for atomic operations that return the previous value. See for example the C/C++11 atomic ops.
The problem I see is that we don't really have the fetch_*() naming in the kernel right now, while we do have the xchg_*() naming. The latter is 'obviously' an atomic operation - while 'fetch' could be anything.
We don't have xchg_*() naming, we have xchg() and that's about it. And yes, people know xchg() is an atomic op. But 'fetch (and) or' is also atomic, it has to be, it needs to do 2 operations in 1.
Furthermore, the relevant wikipedia page is:
https://en.wikipedia.org/wiki/Fetch-and-add
So the naming is widely established.
Peter Zijlstra
maintainerRe: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 5 lines · Ingo Molnar
So if we are still on the same page at this point, we'd have to add a pointer variant too I suspect:
cmpxchg_ptr() xchg_ptr()
We typically call those _long(), like atomic_long_t etc.. Under the assumption that sizeof(long) == sizeof(void *).
Also, _IF_ we're going to do this, this is the perfect opportunity to also introduce the __atomic address space and load() and store() operations. This is needed for architectures where:
cmpxchg()/xchg()
isn't safe against regular stores, like all those who implement atomics using hashed spinlocked.
Its going to be a horrid lot of work though :/
Peter Zijlstra
maintainerRe: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 2 lines · Ingo Molnar
- Renaming
fetch_or()toxchg_or(), recognizing that the semantics are xchg()-alike.
Let me add another argument for why I don't like the "exchange/swap (and) add" naming.
Exchange (and swap) replace one value for another, like:
old = xchg(ptr, val);
Whatever was there, gets replaced by the independent value in @val. Straight up replacement.
However with something like xchg_or, you don't do a direct replacement with an unrelated value. Instead you modify the pre-existing value. So there really isn't an exchange at all.
So "fetch (and) or" really describes the operation better. You load (fetch) the value and then modify it, in an indivisible (aka atomic) fashion.
Re: [PATCH] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 4 lines · Ingo Molnar
I can see two other problems with it as well:
1) 'ptr' may get evaluated multiple times as well, not just 'mask'.
Right you are.
Quoted 2 lines
2) its naming sucks. "
fetch_or()" does not really signal that it's a fundamentally atomic operation, nor what API family it belongs to.
Actually, I like the naming, I just don't like the implementation.
It's not just the "evaluated multiple times", it's also the fact that if we start doing "fetch_or()", then some day we'll want "fetch_and()" or "fetch_clear()" etc.
Or, in fact, "fetch_add()", which is often closer to what hardware does than the "atomic_add_return()" we have now (the difference is that "fetch_add()" returns the original value, while "atomic_add_return()" returns the end result).
So what I meant with "try to match what we already have in our existing atomic.h" is that we do the _infrastructure_ so well. We do it well both by having separate UP and SMP versions of the underlying helpers, but we do it well by having those helpers that are then used to implement the different atomic versions.
So I'd like something similar for the "fetch_op" thing.
Linus
Re: [PATCH] nohz: Change tick_dep_mask from 'unsigned long' to 'unsigned int'
In reply to Ingo MolnarQuoted 7 lines · Ingo Molnar
Hm, so the problem I did not consider is the following:
triton:~/tip> git grep
tick_dep_maskkernel | grep_bitkernel/time/tick-sched.c: clear_bit(bit, &ts->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &tsk->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &sig->tick_dep_mask);
Ahh. I missed that too.
Ok, leave the 64-bit field for now, while we think about this. But one option is to just use "atomic_andnot()" instead of clear_bit().
That would imply using an "atomic_t", which would be fairly natural (and would be 32-bit).
And wouldn't it be so nice if "thread_info->flags" would just be atomic_t too. Right now we use a mixture of bit-ops and ACCESS_ONCE() (and many codepaths then doing neither, and just accessing it directly, ignoring any races.
Oh well.
Linus
Re: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarThanks a lot for fixing this!
Some comments below:
Quoted 19 lines · Ingo Molnar
Linus noticed a couple of problems with the
fetch_or()implementation introduced by5fd7a09cfb8c("atomic: Exportfetch_or()"):- Sloppy macro implementation: 'mask' and 'ptr' is evaluated multiple times, which will break if arguments have side effects. Also, it uses double-underscore temporary variables - which is dangerous as low level asm ones are using those too and they may alias in unexpected ways.
- Sloppy semantics: the naming and structure of the macro is ambigious, with no well-defined semantics. It's neither an atomic nor a
cmpxchg()interface, but still it lives ininclude/linux/atomic.h.Solve this by:
- Extracting the arguments into helper variables once, and never using the original arguments from that point on. This makes it pretty clear that there can be no unwanted macro evaluation side effects.
Quoted 4 lines
- Using single-underscore temporary variables.
- Renaming
fetch_or()toxchg_or(), recognizing that the semantics are xchg()-alike.
I wouldn't mind that much having xchg_or() but I think Peterz has good arguments in favour of keeping the original name.
Quoted 36 lines
Also propagate the rename to the call sites.
Reported-by Linus Torvalds <[email protected]>Cc Andrew Morton <[email protected]>Cc Chris Metcalf <[email protected]>Cc Christoph Lameter <[email protected]>Cc Frederic Weisbecker <[email protected]>Cc Luiz Capitulino <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Rik van Riel <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Viresh Kumar <[email protected]>Signed-off-by Ingo Molnar <[email protected]>--- include/linux/atomic.h | 26 ++++++++++++++++---------- kernel/sched/core.c | 2 +- kernel/time/tick-sched.c | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-)include/linux/atomic.h +2
-2@@ -549,22 +549,28 @@ static inline int atomic_dec_if_positive(atomic_t *v)#endif /** -* fetch_or - perform *ptr |= mask and return old value of *ptr -* @ptr: pointer to value +* xchg_or - perform *ptr |= mask atomically and return old value of *ptr +* @ptr: pointer to value (cmpxchg() compatible integer pointer type) * @mask: mask to OR on the value *
include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 561
Quoted 10 lines
include/linux/atomic.h +5
-4-* cmpxchg based fetch_or, macro so it works for different integer types +* cmpxchg() based, it's a macro so it works for different integer types. */ -#ifndef fetch_or -#define fetch_or (ptr, mask) \ -({ typeof(*(ptr)) __old, __val = *(ptr); \ +#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) _ptr = (ptr); \
Can we add a comment above to prevent from future mistakes with cmpxchg variables aliasing?
I'm suprised that GCC doesn't warn about that actually.
Re: [PATCH] nohz: Change tick_dep_mask from 'unsigned long' to 'unsigned int'
In reply to Linus TorvaldsQuoted 8 lines · Linus Torvalds
Quoted 7 lines · Ingo Molnar
Hm, so the problem I did not consider is the following:
triton:~/tip> git grep
tick_dep_maskkernel | grep_bitkernel/time/tick-sched.c: clear_bit(bit, &ts->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &tsk->tick_dep_mask); kernel/time/tick-sched.c: clear_bit(bit, &sig->tick_dep_mask);
Ah! Now I remember why I used unsigned long :-s
Quoted 8 lines
Ahh. I missed that too.
Ok, leave the 64-bit field for now, while we think about this. But one option is to just use "
atomic_andnot()" instead ofclear_bit().That would imply using an "
atomic_t", which would be fairly natural (and would be 32-bit).
I can try that. And then we would end up with atomic_fetch_or() and maybe the scheduler could keep its ad-hoc version until thread_info types get unified (if that's possible).
Quoted 7 lines
And wouldn't it be so nice if "
thread_info->flags" would just beatomic_ttoo. Right now we use a mixture of bit-ops andACCESS_ONCE()(and many codepaths then doing neither, and just accessing it directly, ignoring any races.Oh well.
I can try that too while at it :-)
Re: [PATCH] nohz: Change tick_dep_mask from 'unsigned long' to 'unsigned int'
Quoted 9 lines · Frederic Weisbecker
Quoted 7 lines
And wouldn't it be so nice if "
thread_info->flags" would just beatomic_ttoo. Right now we use a mixture of bit-ops andACCESS_ONCE()(and many codepaths then doing neither, and just accessing it directly, ignoring any races.Oh well.
I can try that too while at it :-)
The real problem with thread_info->flags is that it's per-architecture, and people access that thing from assembly code.
So changing that is potentially really *very* painful. The patch itself is not likely too bad, but having confidence that everything was caught?
Nasty. It would probably be a very good cleanup, but I'm not sure how worthwhile it is considering the pain.
(The only good news is that any problem is likely to be *very* obvious. Switching the field to a 32-bit type on a big-endian machine and not catching some case will mean that people will check or modify the wrong flags, and you'd likely get breakage quite quickly)
Linus
Ingo Molnar
maintainerRe: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Peter ZijlstraQuoted 14 lines · Peter Zijlstra
Quoted 2 lines · Ingo Molnar
- Renaming
fetch_or()toxchg_or(), recognizing that the semantics are xchg()-alike.Let me add another argument for why I don't like the "exchange/swap (and) add" naming.
Exchange (and swap) replace one value for another, like:
old = xchg(ptr, val);Whatever was there, gets replaced by the independent value in @val. Straight up replacement.
Quoted 7 lines
However with something like
xchg_or, you don't do a direct replacement with an unrelated value. Instead you modify the pre-existing value. So there really isn't an exchange at all.So "fetch (and) or" really describes the operation better. You load (fetch) the value and then modify it, in an indivisible (aka atomic) fashion.
Ok!
Could we at least somehow sneak the notion of 'atomicity' into it?
fetch_or() fetch_and() fetch_not()
vs.
fetch_atomic_or()
fetch_atomic_and()
fetch_atomic_not()
vs.
atomic_fetch_or()
atomic_fetch_and()
atomic_fetch_not()
?
Thanks,
Ingo
Ingo Molnar
maintainerRe: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Frederic WeisbeckerQuoted 28 lines · Frederic Weisbecker
Quoted 14 lines
include/linux/atomic.h +2
-2@@ -549,22 +549,28 @@ static inline int atomic_dec_if_positive(atomic_t *v)#endif /** -* fetch_or - perform *ptr |= mask and return old value of *ptr -* @ptr: pointer to value +* xchg_or - perform *ptr |= mask atomically and return old value of *ptr +* @ptr: pointer to value (cmpxchg() compatible integer pointer type) * @mask: mask to OR on the value * include/linux/atomic.h static inline int atomic_dec_if_positive(atomic_t *v) · line 561
Quoted 10 lines
include/linux/atomic.h +5
-4-* cmpxchg based fetch_or, macro so it works for different integer types +* cmpxchg() based, it's a macro so it works for different integer types. */ -#ifndef fetch_or -#define fetch_or (ptr, mask) \ -({ typeof(*(ptr)) __old, __val = *(ptr); \ +#ifndef xchg_or +# define xchg_or (ptr, mask) \ +({ \ + typeof(ptr) _ptr = (ptr); \Can we add a comment above to prevent from future mistakes with cmpxchg variables aliasing?
Quoted 1 line
I'm suprised that GCC doesn't warn about that actually.
Yeah, so in the perf tooling build we do have -Wshadow to catch such mishaps, but not in the main kernel build.
... and yes, if I add it via the patch below the bug gets warned about:
include/linux/atomic.h:561:15: note: shadowed declaration is here
typeof(ptr) __ptr = (ptr); \
^ kernel/sched/core.c:332:11: note: in expansion of macro ‘xchg_or’
return !(xchg_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLA
... but I also get a ton of other warnings, just when building a single kernel/sched/core.o file:
./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow] ./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow] include/linux/jiffies.h:422:60: warning: declaration of ‘jiffies’ shadows a global declaration [-Wshadow] ./arch/x86/include/asm/io_apic.h:187:54: warning: declaration of ‘apic’ shadows a global declaration [-Wshadow] ./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow] include/linux/jiffies.h:422:60: warning: declaration of ‘jiffies’ shadows a global declaration [-Wshadow] ./arch/x86/include/asm/io_apic.h:187:54: warning: declaration of ‘apic’ shadows a global declaration [-Wshadow] include/linux/kernel.h:750:12: warning: declaration of ‘_min1’ shadows a previous local [-Wshadow] include/linux/kernel.h:750:12: warning: declaration of ‘_min1’ shadows a previous local [-Wshadow] include/linux/kernel.h:751:12: warning: declaration of ‘_min2’ shadows a previous local [-Wshadow] kernel/sched/sched.h:308:43: warning: declaration of ‘down’ shadows a global declaration [-Wshadow] kernel/sched/sched.h:308:60: warning: declaration of ‘up’ shadows a global declaration [-Wshadow] kernel/sched/auto_group.h:44:55: warning: declaration of ‘init_task’ shadows a global declaration [-Wshadow] kernel/sched/core.c:635:20: warning: declaration of ‘down’ shadows a global declaration [-Wshadow] kernel/sched/core.c:635:37: warning: declaration of ‘up’ shadows a global declaration [-Wshadow]
and yes, I'd say most of these are signatures of sloppy macros and sloppy variable names - but it would be a ton of work to eliminate these warnings.
Thanks,
Ingo
==============>
Makefile | 3 +++ 1 file changed, 3 insertions(+)
Makefile +3 -0
@@ -776,6 +776,9 @@ KBUILD_CFLAGS += $(call cc-option,-Werror=date-time) # use the deterministic mode of AR if available KBUILD_ARFLAGS := $(call ar-option,D) +# enforce correct pointer usage +KBUILD_CFLAGS += $(call cc-option,-Wshadow) + # check for 'asm goto' ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y) KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO
Peter Zijlstra
maintainerRe: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 3 lines · Ingo Molnar
atomic_fetch_or()
atomic_fetch_and()
atomic_fetch_not()
Sure we can go add those. I have a flight scheduled early next month, I'll try and have a go at adding all the fetch_$op variants to all arches.
Re: [PATCH v2] atomic: Fix bugs in 'fetch_or()' and rename it to 'xchg_or()'
In reply to Ingo MolnarQuoted 14 lines · Ingo Molnar
Yeah, so in the perf tooling build we do have -Wshadow to catch such mishaps, but not in the main kernel build.
... and yes, if I add it via the patch below the bug gets warned about:
include/linux/atomic.h:561:15: note: shadowed declaration is heretypeof(ptr) __ptr = (ptr); \ ^
kernel/sched/core.c:332:11: note: in expansion of macro ‘xchg_or’
return !(xchg_or(&ti->flags,_TIF_NEED_RESCHED) &_TIF_POLLING_NRFLA... but I also get a ton of other warnings, just when building a single
kernel/sched/core.ofile:
Quoted 15 lines
./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow]./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow]include/linux/jiffies.h:422:60: warning: declaration of ‘jiffies’ shadows a global declaration [-Wshadow]./arch/x86/include/asm/io_apic.h:187:54: warning: declaration of ‘apic’ shadows a global declaration [-Wshadow]./arch/x86/include/asm/bitops.h:396:28: warning: declaration of ‘ffs’ shadows a built-in function [-Wshadow]include/linux/jiffies.h:422:60: warning: declaration of ‘jiffies’ shadows a global declaration [-Wshadow]./arch/x86/include/asm/io_apic.h:187:54: warning: declaration of ‘apic’ shadows a global declaration [-Wshadow]include/linux/kernel.h:750:12: warning: declaration of ‘_min1’ shadows a previous local [-Wshadow]include/linux/kernel.h:750:12: warning: declaration of ‘_min1’ shadows a previous local [-Wshadow]include/linux/kernel.h:751:12: warning: declaration of ‘_min2’ shadows a previous local [-Wshadow]kernel/sched/sched.h:308:43: warning: declaration of ‘down’ shadows a global declaration [-Wshadow]kernel/sched/sched.h:308:60: warning: declaration of ‘up’ shadows a global declaration [-Wshadow]kernel/sched/auto_group.h:44:55: warning: declaration of ‘init_task’ shadows a global declaration [-Wshadow]kernel/sched/core.c:635:20: warning: declaration of ‘down’ shadows a global declaration [-Wshadow]kernel/sched/core.c:635:37: warning: declaration of ‘up’ shadows a global declaration [-Wshadow]
Heh, what else can we expect from global functions named up() and down() in a millions-lines C project :-)
Quoted 3 lines
and yes, I'd say most of these are signatures of sloppy macros and sloppy variable names - but it would be a ton of work to eliminate these warnings.
Yeah that's what I was afraid of.
Thanks for trying it though!