nohz/cpuset: Make a CPU stick with do_timer() duty in the presence of nohz cpusets
Subsystems (2)
PATCH 1/1 nohz/cpuset: Make a CPU stick with do_timer() duty in the presence of nohz cpusets
An adaptive nohz (AHZ) CPU may not do do_timer() for a while despite being non-idle. When all other CPUs are idle, AHZ CPUs might be using stale jiffies values. To prevent this always keep a CPU with ticks if there is one or more AHZ CPUs.
The patch changes can_stop_{idle,adaptive}_tick functions and prevents either the last CPU who did the do_timer() duty or the AHZ CPU itself from stopping its sched timer if there is one or more AHZ CPUs in the system. This means AHZ CPUs might keep the ticks running for short periods until a non-AHZ CPU takes the charge away in tick_do_timer_check_handler() function. When a non-AHZ CPU takes the charge, it never gives it away so that AHZ CPUs can run tickless.
--- include/linux/cpuset.h | 3 ++- kernel/cpuset.c | 5 +++++ kernel/time/tick-sched.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-)
include/linux/cpuset.h +2 -1
@@ -266,11 +266,12 @@ static inline bool cpuset_adaptive_nohz(void)extern void cpuset_exit_nohz_interrupt (void *unused);extern void cpuset_nohz_flush_cputimes (void ); +extern bool nohz_cpu_exist (void );#else static inline bool cpuset_cpu_adaptive_nohz (int cpu) {return false; }static inline bool cpuset_adaptive_nohz (void ) {return false; }static inline void cpuset_nohz_flush_cputimes (void ) { } - +static inline bool nohz_cpu_exist (void ) {return false; }#endif /* CONFIG_CPUSETS_NO_HZ */ #endif /* _LINUX_CPUSET_H */
kernel/cpuset.c +5 -0
@@ -1231,6 +1231,11 @@ DEFINE_PER_CPU(int, cpu_adaptive_nohz_ref);static cpumask_t nohz_cpuset_mask; +inline bool nohz_cpu_exist (void ) +{ +return !cpumask_empty (&nohz_cpuset_mask); +} +static void flush_cputime_interrupt (void *unused) {trace_printk ("IPI: flush cputime\n" );
kernel/time/tick-sched.c +30 -1
@@ -409,6 +409,25 @@ out:return ret; } +static inline bool must_take_timer_duty (int cpu) +{ +int handler = tick_do_timer_cpu; +bool ret = false; +bool tick_needed =nohz_cpu_exist (); + +/* +* A CPU will have to take the timer duty if there is an adaptive +* nohz CPU in the system. The last handler == cpu check ensures +* that the last cpu that did the do_timer() sticks with the duty. +* A normal (non nohz) cpu will take the charge from a nohz cpu in +* tick_do_timer_check_handler anyway. +*/ +if (tick_needed && (handler ==TICK_DO_TIMER_NONE || handler == cpu)) + ret = true; + +return ret; +} +static bool can_stop_idle_tick (int cpu,struct tick_sched *ts) {/*
@@ -421,6 +440,9 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)if (unlikely (!cpu_online (cpu))) {if (cpu == tick_do_timer_cpu) tick_do_timer_cpu =TICK_DO_TIMER_NONE ; + }else if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false; }if (unlikely (ts->nohz_mode ==NOHZ_MODE_INACTIVE ))
@@ -512,6 +534,13 @@ void tick_nohz_idle_enter(void)#ifdef CONFIG_CPUSETS_NO_HZ static bool can_stop_adaptive_tick (void ) { +int cpu =smp_processor_id (); + +if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false; + } +if (!sched_can_stop_tick ())return false;
@@ -519,7 +548,7 @@ static bool can_stop_adaptive_tick(void)return false;/* Is there a grace period to complete ? */ -if (rcu_pending (smp_processor_id ())) +if (rcu_pending (cpu))return false;return true;
Hi Hakan,
As I start to focus on timekeeing for full dynticks, I'm looking at your patch. Sorry I haven't yet replied with a serious review until now. But here is it, finally:
2012/6/17 Hakan Akkan <[email protected]>:
Quoted 63 lines
An adaptive nohz (AHZ) CPU may not do
do_timer()for a while despite being non-idle. When all other CPUs are idle, AHZ CPUs might be using stale jiffies values. To prevent this always keep a CPU with ticks if there is one or more AHZ CPUs.The patch changes can_stop_{idle,adaptive}_tick functions and prevents either the last CPU who did the
do_timer()duty or the AHZ CPU itself from stopping its sched timer if there is one or more AHZ CPUs in the system. This means AHZ CPUs might keep the ticks running for short periods until a non-AHZ CPU takes the charge away intick_do_timer_check_handler()function. When a non-AHZ CPU takes the charge, it never gives it away so that AHZ CPUs can run tickless.Signed-off-by Hakan Akkan <[email protected]>CC Frederic Weisbecker <[email protected]>--- include/linux/cpuset.h | 3 ++- kernel/cpuset.c | 5 +++++ kernel/time/tick-sched.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-)include/linux/cpuset.h +2
-1@@ -266,11 +266,12 @@ static inline bool cpuset_adaptive_nohz(void)extern void cpuset_exit_nohz_interrupt (void *unused);extern void cpuset_nohz_flush_cputimes (void ); +extern bool nohz_cpu_exist (void );#else static inline bool cpuset_cpu_adaptive_nohz (int cpu) {return false; }static inline bool cpuset_adaptive_nohz (void ) {return false; }static inline void cpuset_nohz_flush_cputimes (void ) { } - +static inline bool nohz_cpu_exist (void ) {return false; }#endif /* CONFIG_CPUSETS_NO_HZ */ #endif /* _LINUX_CPUSET_H */ kernel/cpuset.c +5
-0@@ -1231,6 +1231,11 @@ DEFINE_PER_CPU(int, cpu_adaptive_nohz_ref);static cpumask_t nohz_cpuset_mask; +inline bool nohz_cpu_exist (void ) +{ +return !cpumask_empty (&nohz_cpuset_mask); +} +static void flush_cputime_interrupt (void *unused) {trace_printk ("IPI: flush cputime\n" );kernel/time/tick-sched.c +0
-0
kernel/time/tick-sched.c out: · line 416
Quoted 8 lines
kernel/time/tick-sched.c +5
-0@@ -409,6 +409,25 @@ out:return ret; } +static inline bool must_take_timer_duty (int cpu) +{ +int handler = tick_do_timer_cpu; +bool ret = false; +bool tick_needed =nohz_cpu_exist ();
Note this is racy because this fetches the value of the nohz_cpuset_mask without locking.
We may "see" there is no nohz cpusets whereas we just set some of them as nohz and they
could even have shut down their tick already.
kernel/time/tick-sched.c out: · line 426
Quoted 10 lines
kernel/time/tick-sched.c +10
-0+ +/* +* A CPU will have to take the timer duty if there is an adaptive +* nohz CPU in the system. The last handler == cpu check ensures +* that the last cpu that did the do_timer() sticks with the duty. +* A normal (non nohz) cpu will take the charge from a nohz cpu in +* tick_do_timer_check_handler anyway. +*/ +if (tick_needed && (handler ==TICK_DO_TIMER_NONE || handler == cpu)) + ret = true;
This check is also racy due to the lack of locking. The previous handler may have set TICK_DO_TIMER_NONE and gone to sleep. We have no guarantee that the CPU can see that new value. It could believe there is still a handler. This needs at least cmpxchg() to make the test and set atomic.
Quoted 18 lines
kernel/time/tick-sched.c +7
-0+ +return ret; +} +static bool can_stop_idle_tick (int cpu,struct tick_sched *ts) {/* @@ -421,6 +440,9 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)if (unlikely (!cpu_online (cpu))) {if (cpu == tick_do_timer_cpu) tick_do_timer_cpu =TICK_DO_TIMER_NONE ; + }else if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false; }if (unlikely (ts->nohz_mode ==NOHZ_MODE_INACTIVE ))
kernel/time/tick-sched.c void tick_nohz_idle_enter(void) · line 541
Quoted 8 lines
kernel/time/tick-sched.c +5
-0@@ -512,6 +534,13 @@ void tick_nohz_idle_enter(void)#ifdef CONFIG_CPUSETS_NO_HZ static bool can_stop_adaptive_tick (void ) { +int cpu =smp_processor_id (); + +if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false;
One problem I see here is that you randomize the handler. It could be an adaptive nohz CPU or an idle CPU. It's a problem if the user wants CPU isolation.
I suggest to rather define a tunable timekeeping duty CPU affinity in a cpumask file at /sys/devices/system/cpu/timekeeping and a toggle at /sys/devices/system/cpu/cpuX/timekeeping (like the online file). This way the user can decide whether adaptive nohz CPU can handle timekeeping or this must be forced to other CPUs in order to enforce isolation.
Concerning implementation details, a way to avoid locking while ensuring there is always a CPU handling timekeeping is to define some state machine through tick_do_timer_cpu values like the below scenario:
* tick_do_timer_cpu = some CPU. In this case the timekeeping has a running handler, everything is fine
* tick_do_timer_cpu = TICK_DO_TIMER_IDLE. The timekeeping handler wants to go idle and stop its tick. He's going to keep its tick and the timekeeping duty until some other CPU sees this value and sets itself as the new handler. Then the previous handler can finally stop its tick.
* tick_do_timer_cpu = TICK_DO_TIMER_NONE. There is no handler currently. The next handler is the next CPU that will restart its tick. This value is only possible if cpu_online_mask = cpu_timekeeping_mask and there is no adaptive nohz running.
Does that look ok?
If so, tell me if you want to rework this patch with these suggestions. Or I can do it, either way.
Thanks.
Quoted 74 lines · Frederic Weisbecker
Hi Hakan,
As I start to focus on timekeeing for full dynticks, I'm looking at your patch. Sorry I haven't yet replied with a serious review until now. But here is it, finally:
2012/6/17 Hakan Akkan <[email protected]>:
Quoted 66 lines
An adaptive nohz (AHZ) CPU may not do
do_timer()for a while despite being non-idle. When all other CPUs are idle, AHZ CPUs might be using stale jiffies values. To prevent this always keep a CPU with ticks if there is one or more AHZ CPUs.The patch changes can_stop_{idle,adaptive}_tick functions and prevents either the last CPU who did the
do_timer()duty or the AHZ CPU itself from stopping its sched timer if there is one or more AHZ CPUs in the system. This means AHZ CPUs might keep the ticks running for short periods until a non-AHZ CPU takes the charge away intick_do_timer_check_handler()function. When a non-AHZ CPU takes the charge, it never gives it away so that AHZ CPUs can run tickless.Signed-off-by Hakan Akkan <[email protected]>CC Frederic Weisbecker <[email protected]>--- include/linux/cpuset.h | 3 ++- kernel/cpuset.c | 5 +++++ kernel/time/tick-sched.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-)include/linux/cpuset.h +2
-1@@ -266,11 +266,12 @@ static inline bool cpuset_adaptive_nohz(void)extern void cpuset_exit_nohz_interrupt (void *unused);extern void cpuset_nohz_flush_cputimes (void ); +extern bool nohz_cpu_exist (void );#else static inline bool cpuset_cpu_adaptive_nohz (int cpu) {return false; }static inline bool cpuset_adaptive_nohz (void ) {return false; }static inline void cpuset_nohz_flush_cputimes (void ) { } - +static inline bool nohz_cpu_exist (void ) {return false; }#endif /* CONFIG_CPUSETS_NO_HZ */ #endif /* _LINUX_CPUSET_H */ kernel/cpuset.c +5
-0@@ -1231,6 +1231,11 @@ DEFINE_PER_CPU(int, cpu_adaptive_nohz_ref);static cpumask_t nohz_cpuset_mask; +inline bool nohz_cpu_exist (void ) +{ +return !cpumask_empty (&nohz_cpuset_mask); +} +static void flush_cputime_interrupt (void *unused) {trace_printk ("IPI: flush cputime\n" );kernel/time/tick-sched.c +0
-0@@ -409,6 +409,25 @@ out:return ret; }
Quoted 11 lines
kernel/time/tick-sched.c out: · line 416
Quoted 5 lines
kernel/time/tick-sched.c +5
-0+static inline bool must_take_timer_duty (int cpu) +{ +int handler = tick_do_timer_cpu; +bool ret = false; +bool tick_needed =nohz_cpu_exist ();Note this is racy because this fetches the value of the
nohz_cpuset_maskwithout locking.
We may "see" there is no nohz cpusets whereas we just set some of them as nohz and they
could even have shut down their tick already.
Yes, this is racy indeed. Thanks for pointing out.
Quoted 35 lines
kernel/time/tick-sched.c out: · line 426
Quoted 12 lines
kernel/time/tick-sched.c +10
-0+ +/* +* A CPU will have to take the timer duty if there is an adaptive +* nohz CPU in the system. The last handler == cpu check ensures +* that the last cpu that did the do_timer() sticks with the duty. +* A normal (non nohz) cpu will take the charge from a nohz cpu in +* tick_do_timer_check_handler anyway. +*/ +if (tick_needed && (handler ==TICK_DO_TIMER_NONE || handler == cpu)) + ret = true;This check is also racy due to the lack of locking. The previous handler may have set
TICK_DO_TIMER_NONEand gone to sleep. We have no guarantee that the CPU can see that new value. It could believe there is still a handler. This needs at leastcmpxchg()to make the test and set atomic.Quoted 16 lines
kernel/time/tick-sched.c +7
-0+ +return ret; +} +static bool can_stop_idle_tick (int cpu,struct tick_sched *ts) {/* @@ -421,6 +440,9 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)if (unlikely (!cpu_online (cpu))) {if (cpu == tick_do_timer_cpu) tick_do_timer_cpu =TICK_DO_TIMER_NONE ; + }else if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false; }
Quoted 14 lines
kernel/time/tick-sched.c void tick_nohz_idle_enter(void) · line 541
Quoted 10 lines
kernel/time/tick-sched.c +5
-0if (unlikely (ts->nohz_mode ==NOHZ_MODE_INACTIVE ))@@ -512,6 +534,13 @@ void tick_nohz_idle_enter(void)#ifdef CONFIG_CPUSETS_NO_HZ static bool can_stop_adaptive_tick (void ) { +int cpu =smp_processor_id (); + +if (must_take_timer_duty (cpu)) { + tick_do_timer_cpu = cpu; +return false;One problem I see here is that you randomize the handler. It could be an adaptive nohz CPU or an idle CPU. It's a problem if the user wants CPU isolation.
Yes, an adaptive nohz CPU might be forced to take the duty if nobody else is doing it. However, as soon as a regular CPU notices this it takes the duty and never gives it up as long as there are nohz cpusets in the system.
Quoted 7 lines
I suggest to rather define a tunable timekeeping duty CPU affinity in a cpumask file at
/sys/devices/system/cpu/timekeepingand a toggle at/sys/devices/system/cpu/cpuX/timekeeping(like the online file). This way the user can decide whether adaptive nohz CPU can handle timekeeping or this must be forced to other CPUs in order to enforce isolation.
Well, users want tickless CPUs because they don't want timekeeping (or any other kernel activity for that matter) to run in there. So, I believe having that "timekeeping affinity" stay in the regular CPUs is good enough. Please let me know how users could utilize these control files to do anything other than keeping the timekeeping out of adaptive nohz CPUs.
Quoted 15 lines
Concerning implementation details, a way to avoid locking while ensuring there is always a CPU handling timekeeping is to define some state machine through
tick_do_timer_cpuvalues like the below scenario:*
tick_do_timer_cpu= some CPU. In this case the timekeeping has a running handler, everything is fine*
tick_do_timer_cpu=TICK_DO_TIMER_IDLE. The timekeeping handler wants to go idle and stop its tick. He's going to keep its tick and the timekeeping duty until some other CPU sees this value and sets itself as the new handler. Then the previous handler can finally stop its tick.
Quoted 6 lines
*
tick_do_timer_cpu=TICK_DO_TIMER_NONE. There is no handler currently. The next handler is the next CPU that will restart its tick. This value is only possible ifcpu_online_mask=cpu_timekeeping_maskand there is no adaptive nohz running.Does that look ok?
Something like this together with a mechanism to prevent regular CPUs stopping the tick for being idle when adaptive nohz CPUs are doing their things is necessary. I'll rework the patch and resend soon.
Quoted 5 lines
If so, tell me if you want to rework this patch with these suggestions. Or I can do it, either way.
Thanks.
Thanks.
Quoted 8 lines · Hakan Akkan
Quoted 7 lines
I suggest to rather define a tunable timekeeping duty CPU affinity in a cpumask file at
/sys/devices/system/cpu/timekeepingand a toggle at/sys/devices/system/cpu/cpuX/timekeeping(like the online file). This way the user can decide whether adaptive nohz CPU can handle timekeeping or this must be forced to other CPUs in order to enforce isolation.
Quoted 5 lines
Well, users want tickless CPUs because they don't want timekeeping (or any other kernel activity for that matter) to run in there. So, I believe having that "timekeeping affinity" stay in the regular CPUs is good enough. Please let me know how users could utilize these control files to do anything other than keeping the timekeeping out of adaptive nohz CPUs.
I agree. If we already have some /sys cpumask that denotes which CPUs will be adaptive NO_HZ (or simply isolated) then just keep the tick from ever going on those CPUs. If all but one CPU is set for nohz, and that one CPU goes idle, it should still be the one doing the tick.
-- Steve
2012/11/20 Steven Rostedt <[email protected]>:
Quoted 16 lines · Hakan Akkan
Quoted 9 lines
Quoted 8 lines
I suggest to rather define a tunable timekeeping duty CPU affinity in a cpumask file at
/sys/devices/system/cpu/timekeepingand a toggle at/sys/devices/system/cpu/cpuX/timekeeping(like the online file). This way the user can decide whether adaptive nohz CPU can handle timekeeping or this must be forced to other CPUs in order to enforce isolation.Quoted 5 lines
Well, users want tickless CPUs because they don't want timekeeping (or any other kernel activity for that matter) to run in there. So, I believe having that "timekeeping affinity" stay in the regular CPUs is good enough. Please let me know how users could utilize these control files to do anything other than keeping the timekeeping out of adaptive nohz CPUs.
Quoted 4 lines
I agree. If we already have some /sys cpumask that denotes which CPUs will be adaptive
NO_HZ(or simply isolated) then just keep the tick from ever going on those CPUs. If all but one CPU is set for nohz, and that one CPU goes idle, it should still be the one doing the tick.
If you want isolation on your full dynticks CPU it's right. Now you could have lower requirements, a different policy that rather enforce energy saving.
But I realize we can integrate such granularity later if users request it and take the behaviour you both describe as the default for now. So let's take that direction.
Thanks.
PATCH 1/1 nohz/cpuset: Make a CPU stick with do_timer() duty in the presence of nohz cpusets
An adaptive nohz (AHZ) CPU may not do do_timer() for a while despite being non-idle. When all other CPUs are idle, AHZ CPUs might be using stale jiffies values. To prevent this always keep a CPU with ticks if there is one or more AHZ CPUs.
A new function, check_drop_timer_duty, handles the updates to tick_do_timer_cpu value and makes sure that the jiffies update is done when there are non-idle adaptive-nohz CPUs. Also added is a new field in struct tick_sched to indicate if CPU is ready to run something other than the idle task without ticks once it drops the do_timer() duty. This also facilitates the system-wide tick shut down when all CPUs, including AHZ CPUs, are idle.
--- include/linux/tick.h | 2 + kernel/time/tick-sched.c | 235 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 188 insertions(+), 49 deletions(-)
include/linux/tick.h +2 -0
@@ -54,6 +54,7 @@ enum tick_saved_jiffies {* @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding * @sleep_length: Duration of the current idle sleep * @do_timer_lst: CPU was the last one doing do_timer before going idle +* @user_nohz: CPU wants to switch to adaptive nohz mode */ struct tick_sched {struct hrtimer sched_timer;
@@ -77,6 +78,7 @@ struct tick_sched {unsigned long next_jiffies;ktime_t idle_expires;int do_timer_last; +int user_nohz; };extern void __init tick_init (void );
kernel/time/tick-sched.c +186 -49
313 lines not on this page · raw message
Signature · 1 line
1.7.10.4
PATCH 1/1 nohz/cpuset: Make a CPU stick with do_timer() duty in the presence of nohz cpusets
(Please ignore the previous patch as it never really prevented the last idler from going longer than timekeeping_max_deferement sleeps.)
An adaptive nohz (AHZ) CPU may not do do_timer() for a while despite being non-idle. When all other CPUs are idle, AHZ CPUs might be using stale jiffies values. To prevent this always keep a CPU with ticks if there is one or more AHZ CPUs.
A new function, check_drop_timer_duty, handles the updates to tick_do_timer_cpu value and makes sure that the jiffies update is done when there are non-idle adaptive-nohz CPUs. Also added is a new field in struct tick_sched to indicate if CPU is ready to run something other than the idle task without ticks once it drops the do_timer() duty. This also facilitates the system-wide tick shut down when all CPUs, including AHZ CPUs, are idle.
--- include/linux/tick.h | 2 + kernel/time/tick-sched.c | 219 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 174 insertions(+), 47 deletions(-)
include/linux/tick.h +2 -0
@@ -54,6 +54,7 @@ enum tick_saved_jiffies {* @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding * @sleep_length: Duration of the current idle sleep * @do_timer_lst: CPU was the last one doing do_timer before going idle +* @user_nohz: CPU wants to switch to adaptive nohz mode */ struct tick_sched {struct hrtimer sched_timer;
@@ -77,6 +78,7 @@ struct tick_sched {unsigned long next_jiffies;ktime_t idle_expires;int do_timer_last; +int user_nohz; };extern void __init tick_init (void );
kernel/time/tick-sched.c +172 -47
295 lines not on this page · raw message
Signature · 1 line
1.7.10.4
2012/11/28 Hakan Akkan <[email protected]>:
Quoted 4 lines
kernel/time/tick-sched.c +4
-0+static int check_drop_timer_duty (int cpu) +{ +int curr_handler, prev_handler, new_handler; +int nrepeat =-1 ;
kernel/time/tick-sched.c update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_upda · line 223
Quoted 10 lines
kernel/time/tick-sched.c +10
-0+bool drop_recheck; + +repeat: +WARN_ON_ONCE (++nrepeat >1 ); + drop_recheck = false; + curr_handler = cpu; + new_handler =TICK_DO_TIMER_NONE ; + +#ifdef CONFIG_CPUSETS_NO_HZ +if (atomic_read (&nr_cpus_user_nohz) >0 ) {
Note atomic_read() is not SMP ordered. If another CPU does atomic_add() or atomic_add_return(), you may not see the new value as expected with atomic_read(). Doing atomic_add_return(0, &atomic_thing) returns the fully ordered result.
You also need to do that to ensure full ordering against tick_cpu_sched.user_nohz.
On the current layout we have:
(Write side) (Read side)
ts->user_nohz = 1;atomic_inc(&nr_cpus_user_nohz)
if
(atomic_read(&nr_cpus_user_nohz))
if
(per_cpu(tick_cpu_sched, curr_handler).user_nohz)
....
If you want to make sure that you see the expected value on user_nohz from the read side, you need to correctly order the write and read against nr_cpus_user_nohz.
For this you can use atomic_add_return() which implies the full barrier:
(Write side) (Read side)
ts->user_nohz = 1;atomic_inc_return(&nr_cpus_user_nohz)
if
(atomic_add_return(0, &nr_cpus_user_nohz))
if
(per_cpu(tick_cpu_sched, curr_handler).user_nohz)
....
I have much more comments on this patch, I will come back on this soon.
Thanks.
Quoted 12 lines · Frederic Weisbecker
2012/11/28 Hakan Akkan <[email protected]>:
Quoted 11 lines
kernel/time/tick-sched.c +11
-0+static int check_drop_timer_duty (int cpu) +{ +int curr_handler, prev_handler, new_handler; +int nrepeat =-1 ; +bool drop_recheck; + +repeat: +WARN_ON_ONCE (++nrepeat >1 ); + drop_recheck = false; + curr_handler = cpu; + new_handler =TICK_DO_TIMER_NONE ;
Quoted 8 lines
kernel/time/tick-sched.c update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_upda · line 223
Quoted 3 lines
kernel/time/tick-sched.c +3
-0+ +#ifdef CONFIG_CPUSETS_NO_HZ +if (atomic_read (&nr_cpus_user_nohz) >0 ) {Note
atomic_read()is not SMP ordered. If another CPU doesatomic_add()oratomic_add_return(), you may not see the new value as expected withatomic_read(). Doingatomic_add_return(0, &atomic_thing)returns the fully ordered result.
That code doesn't necessarily try to achieve a prefect ordering. I wasn't very sure if we want the overhead of atomic_add_return(0, ...) in such a hot path so this part of the function is racy. Nevertheless, it makes sure that someone gets the duty.
#ifdef CONFIG_CPUSETS_NO_HZ
if (atomic_read(&nr_cpus_user_nohz) > 0) {The CPU who updated nr_cpus_user_nohz will certainly see the new value and claim the duty if someone else hasn't yet. Another regular CPU will eventually see the new value and assign itself to the duty. So we can tolerate some race here for the sake of avoiding heavier locks.
Quoted 15 lines
You also need to do that to ensure full ordering against tick_cpu_sched.user_nohz.
On the current layout we have:
(Write side) (Read side)
ts->user_nohz= 1;atomic_inc(&nr_cpus_user_nohz)if
(atomic_read(&nr_cpus_user_nohz))
if
(per_cpu(tick_cpu_sched, curr_handler).user_nohz)
Quoted 5 lines
....If you want to make sure that you see the expected value on
user_nohzfrom the read side, you need to correctly order the write and read againstnr_cpus_user_nohz.
Again, perfect ordering is not critical here. In the worst case we miss that CPUs ts->user_nohz = 1 update and that adaptive nohz CPU will keep the duty for a short while until someone else takes it. Trying to avoid this would require more locking instructions like you suggest below.
If we really want to fully isolate nohz CPUs and avoid "being stuck with jiffies update until someone else takes it away" situations, we can have a cpumask denoting idle and/or non-adaptive-nohz CPUs and dump the duty once we discover that we're "stuck".
Quoted 18 lines
For this you can use
atomic_add_return()which implies the full barrier:(Write side) (Read side)
ts->user_nohz= 1;atomic_inc_return(&nr_cpus_user_nohz)if
(atomic_add_return(0, &nr_cpus_user_nohz))
if
(per_cpu(tick_cpu_sched, curr_handler).user_nohz)....I have much more comments on this patch, I will come back on this soon.
Thanks.