nohz: Move nohz kick out of scheduler IPI, v3
Subsystems (7)
cover nohz: Move nohz kick out of scheduler IPI, v3
Hi,
So this version gives up with smp_queue_function_single() and extends irq work to support remote queuing. As suggested by Peterz.
Comments?
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks.git
timers/nohz-irq-work
Thanks,
Frederic
---
Frederic Weisbecker (5):
irq_work: Architecture support for remote irq work raise
irq_work: Force non-lazy works on IPI
irq_work: Allow remote queueing
nohz: Move full nohz kick to its own IPI
nohz: Use IPI implicit full barrier against rq->nr_running r/w
arch/Kconfig | 12 +++++++ arch/alpha/kernel/time.c | 3 +- arch/arm/Kconfig | 1 + arch/arm/kernel/smp.c | 4 +-- arch/powerpc/kernel/time.c | 3 +- arch/sparc/kernel/pcr.c | 3 +- arch/x86/Kconfig | 1 + arch/x86/kernel/irq_work.c | 10 ++---- include/linux/irq_work.h | 3 ++ include/linux/tick.h | 9 ++++- kernel/irq_work.c | 87 +++++++++++++++++++++++++++++++--------------- kernel/sched/core.c | 14 ++++---- kernel/sched/sched.h | 12 +++++-- kernel/time/Kconfig | 2 ++ kernel/time/tick-sched.c | 10 +++--- kernel/timer.c | 2 +- 16 files changed, 118 insertions(+), 58 deletions(-)
PATCH 1/5 irq_work: Architecture support for remote irq work raise
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.
Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite arch_irq_work_raise() seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.
--- arch/Kconfig | 12 ++++++++++++ arch/alpha/kernel/time.c | 3 ++- arch/arm/Kconfig | 1 + arch/arm/kernel/smp.c | 4 ++-- arch/powerpc/kernel/time.c | 3 ++- arch/sparc/kernel/pcr.c | 3 ++- arch/x86/Kconfig | 1 + arch/x86/kernel/irq_work.c | 10 ++-------- kernel/irq_work.c | 4 ++-- 9 files changed, 26 insertions(+), 15 deletions(-)
arch/Kconfig +12 -0
@@ -472,6 +472,18 @@ config HAVE_IRQ_EXIT_ON_IRQ_STACK This spares a stack switch and improves cache usage on softirq processing. +config HAVE_IRQ_WORK_IPI + bool + help + Architecture supports raising irq work interrupts both locally and + remotely. Without this capability, we can only trigger local irq works + loosely handled by the generic timer tick with the bad implications + coming along: the irq work is subject to HZ latency and it runs under + the tick random locking scenario (possibly holding hrtimer lock). + + This capability is required on configs running with a very minimized + tick rate like full dynticks. + # # ABI hall of shame #
arch/alpha/kernel/time.c +2 -1
@@ -60,8 +60,9 @@ DEFINE_PER_CPU(u8, irq_work_pending);#define test_irq_work_pending () __get_cpu_var(irq_work_pending)#define clear_irq_work_pending () __get_cpu_var(irq_work_pending) = 0 -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { +WARN_ON_ONCE (cpu !=smp_processor_id ());set_irq_work_pending_flag (); }
arch/arm/Kconfig +1 -0
@@ -46,6 +46,7 @@ config ARM select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) select HAVE_IDE if PCI || ISA || PCMCIA select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_IRQ_WORK_IPI select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZ4 select HAVE_KERNEL_LZMA
arch/arm/kernel/smp.c +2 -2
@@ -454,10 +454,10 @@ void arch_send_call_function_single_ipi(int cpu) }#ifdef CONFIG_IRQ_WORK -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) {if (is_smp ()) -smp_cross_call (cpumask_of (smp_processor_id ()),IPI_IRQ_WORK ); +smp_cross_call (cpumask_of (cpu),IPI_IRQ_WORK ); }#endif
arch/powerpc/kernel/time.c +2 -1
@@ -464,9 +464,10 @@ DEFINE_PER_CPU(u8, irq_work_pending);#endif /* 32 vs 64 bit */ -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) {preempt_disable (); +WARN_ON_ONCE (cpu !=smp_processor_id ());set_irq_work_pending_flag ();set_dec (1 );preempt_enable ();
arch/sparc/kernel/pcr.c +2 -1
@@ -43,8 +43,9 @@ void __irq_entry deferred_pcr_work_irq(int irq, struct pt_regs *regs)set_irq_regs (old_regs); } -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { +WARN_ON_ONCE (cpu !=smp_processor_id ());set_softint (1 <<PIL_DEFERRED_PCR_WORK ); }
arch/x86/Kconfig +1 -0
@@ -130,6 +130,7 @@ config X86 select HAVE_CC_STACKPROTECTOR select GENERIC_CPU_AUTOPROBE select HAVE_ARCH_AUDITSYSCALL + select HAVE_IRQ_WORK_IPI config INSTRUCTION_DECODER def_bool y
arch/x86/kernel/irq_work.c +2 -8
@@ -38,13 +38,7 @@ __visible void smp_trace_irq_work_interrupt(struct pt_regs *regs)exiting_irq (); } -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { -#ifdef CONFIG_X86_LOCAL_APIC -if (!cpu_has_apic) -return ; - - apic->send_IPI_self (IRQ_WORK_VECTOR ); -apic_wait_icr_idle (); -#endif + apic->send_IPI_mask (cpumask_of (cpu),IRQ_WORK_VECTOR ); }
kernel/irq_work.c +2 -2
@@ -48,7 +48,7 @@ static bool irq_work_claim(struct irq_work *work)return true; } -void __weak arch_irq_work_raise (void ) +void __weak arch_irq_work_raise (int cpu) {/* * Lame architectures will get the timer tick callback
@@ -79,7 +79,7 @@ bool irq_work_queue(struct irq_work *work)*/ if (!(work->flags &IRQ_WORK_LAZY ) ||tick_nohz_tick_stopped ()) {if (!this_cpu_cmpxchg (irq_work_raised,0 ,1 )) -arch_irq_work_raise (); +arch_irq_work_raise (smp_processor_id ()); }preempt_enable ();
Signature · 1 line
1.8.3.1
Re: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 10 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.
Well, looks like it's time to turn it into an IPI... It gets a bit more tricky because whether whacking the interrupt controller is safe to do from an NMI is safe or not might depend on that irq controller implementation...
It looks like XICS and MPIC should be safe though, so at least we should be able to cover ppc64, but I'll leave ppc32 alone.
Cheers,
Ben.
Quoted 176 lines
Cc Andrew Morton <[email protected]>Cc Benjamin Herrenschmidt <[email protected]>Cc David S. Miller <[email protected]>Cc Ingo Molnar <[email protected]>Cc Kevin Hilman <[email protected]>Cc Paul E. McKenney <[email protected]>Cc Paul Mackerras <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Russell King <[email protected]>Cc Thomas Gleixner <[email protected]>Cc Viresh Kumar <[email protected]>Signed-off-by Frederic Weisbecker <[email protected]>--- arch/Kconfig | 12 ++++++++++++ arch/alpha/kernel/time.c | 3 ++- arch/arm/Kconfig | 1 + arch/arm/kernel/smp.c | 4 ++-- arch/powerpc/kernel/time.c | 3 ++- arch/sparc/kernel/pcr.c | 3 ++- arch/x86/Kconfig | 1 + arch/x86/kernel/irq_work.c | 10 ++-------- kernel/irq_work.c | 4 ++-- 9 files changed, 26 insertions(+), 15 deletions(-)arch/Kconfig +12
-0@@ -472,6 +472,18 @@ config HAVE_IRQ_EXIT_ON_IRQ_STACK This spares a stack switch and improves cache usage on softirq processing. +config HAVE_IRQ_WORK_IPI + bool + help + Architecture supports raising irq work interrupts both locally and + remotely. Without this capability, we can only trigger local irq works + loosely handled by the generic timer tick with the bad implications + coming along: the irq work is subject to HZ latency and it runs under + the tick random locking scenario (possibly holding hrtimer lock). + + This capability is required on configs running with a very minimized + tick rate like full dynticks. + # # ABI hall of shame #arch/alpha/kernel/time.c +2
-1@@ -60,8 +60,9 @@ DEFINE_PER_CPU(u8, irq_work_pending);#define test_irq_work_pending () __get_cpu_var(irq_work_pending)#define clear_irq_work_pending () __get_cpu_var(irq_work_pending) = 0 -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { +WARN_ON_ONCE (cpu !=smp_processor_id ());set_irq_work_pending_flag (); }arch/arm/Kconfig +1
-0@@ -46,6 +46,7 @@ config ARM select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7)) select HAVE_IDE if PCI || ISA || PCMCIA select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_IRQ_WORK_IPI select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZ4 select HAVE_KERNEL_LZMAarch/arm/kernel/smp.c +2
-2@@ -454,10 +454,10 @@ void arch_send_call_function_single_ipi(int cpu) }#ifdef CONFIG_IRQ_WORK -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) {if (is_smp ()) -smp_cross_call (cpumask_of (smp_processor_id ()),IPI_IRQ_WORK ); +smp_cross_call (cpumask_of (cpu),IPI_IRQ_WORK ); }#endif arch/powerpc/kernel/time.c +2
-1@@ -464,9 +464,10 @@ DEFINE_PER_CPU(u8, irq_work_pending);#endif /* 32 vs 64 bit */ -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) {preempt_disable (); +WARN_ON_ONCE (cpu !=smp_processor_id ());set_irq_work_pending_flag ();set_dec (1 );preempt_enable ();arch/sparc/kernel/pcr.c +2
-1@@ -43,8 +43,9 @@ void __irq_entry deferred_pcr_work_irq(int irq, struct pt_regs *regs)set_irq_regs (old_regs); } -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { +WARN_ON_ONCE (cpu !=smp_processor_id ());set_softint (1 <<PIL_DEFERRED_PCR_WORK ); }arch/x86/Kconfig +1
-0@@ -130,6 +130,7 @@ config X86 select HAVE_CC_STACKPROTECTOR select GENERIC_CPU_AUTOPROBE select HAVE_ARCH_AUDITSYSCALL + select HAVE_IRQ_WORK_IPI config INSTRUCTION_DECODER def_bool yarch/x86/kernel/irq_work.c +2
-8@@ -38,13 +38,7 @@ __visible void smp_trace_irq_work_interrupt(struct pt_regs *regs)exiting_irq (); } -void arch_irq_work_raise (void ) +void arch_irq_work_raise (int cpu) { -#ifdef CONFIG_X86_LOCAL_APIC -if (!cpu_has_apic) -return ; - - apic->send_IPI_self (IRQ_WORK_VECTOR ); -apic_wait_icr_idle (); -#endif + apic->send_IPI_mask (cpumask_of (cpu),IRQ_WORK_VECTOR ); }kernel/irq_work.c +2
-2@@ -48,7 +48,7 @@ static bool irq_work_claim(struct irq_work *work)return true; } -void __weak arch_irq_work_raise (void ) +void __weak arch_irq_work_raise (int cpu) {/* * Lame architectures will get the timer tick callback @@ -79,7 +79,7 @@ bool irq_work_queue(struct irq_work *work)*/ if (!(work->flags &IRQ_WORK_LAZY ) ||tick_nohz_tick_stopped ()) {if (!this_cpu_cmpxchg (irq_work_raised,0 ,1 )) -arch_irq_work_raise (); +arch_irq_work_raise (smp_processor_id ()); }preempt_enable ();
Re: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 12 lines · Benjamin Herrenschmidt
Quoted 10 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.
Quoted 7 lines
Well, looks like it's time to turn it into an IPI... It gets a bit more tricky because whether whacking the interrupt controller is safe to do from an NMI is safe or not might depend on that irq controller implementation...
It looks like XICS and MPIC should be safe though, so at least we should be able to cover ppc64, but I'll leave ppc32 alone.
Correction... that's actually a bit more tricky. We might need an MMIO to trigger the IPI. That means potentially having to take a hash miss, and we certainly can't do that at NMI time at the moment.
We *could* hard disable interrupts (which blocks our NMIs since they arent't real NMIs, they are just a way to bypass our soft-disable state for perf interrupts) for hash_page, but that still makes me somewhat nervous.
Another option would be to add an ioremap flag of some description to be able to install bolted hash entries. (It already does so if called early enough during boot, so it might actually just work by accident but that's an undebuggable horror show waiting to happen if we ever change that).
So needs a bit more thinking on our side.
Cheers,
Ben.
Re: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 30 lines · Benjamin Herrenschmidt
Quoted 12 lines · Benjamin Herrenschmidt
Quoted 10 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.Quoted 7 lines
Well, looks like it's time to turn it into an IPI... It gets a bit more tricky because whether whacking the interrupt controller is safe to do from an NMI is safe or not might depend on that irq controller implementation...
It looks like XICS and MPIC should be safe though, so at least we should be able to cover ppc64, but I'll leave ppc32 alone.
Correction... that's actually a bit more tricky. We might need an MMIO to trigger the IPI. That means potentially having to take a hash miss, and we certainly can't do that at NMI time at the moment.
We *could* hard disable interrupts (which blocks our NMIs since they arent't real NMIs, they are just a way to bypass our soft-disable state for perf interrupts) for
hash_page, but that still makes me somewhat nervous.
Quoted 7 lines
Another option would be to add an ioremap flag of some description to be able to install bolted hash entries. (It already does so if called early enough during boot, so it might actually just work by accident but that's an undebuggable horror show waiting to happen if we ever change that).
So needs a bit more thinking on our side.
Yeah, well if we ever end up with native remote irq work, only local raise will need to be NMI-safe. If that ever helps...
Peter Zijlstra
maintainerRe: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
In reply to Frederic WeisbeckerQuoted 6 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.
Quoted 7 lines
Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.Signed-off-by Frederic Weisbecker <[email protected]>
Why not borrow the smp_call_function IPI for the remote bits? We could limit the 'safe from NMI' to the local works. And we validate this by putting a WARN_ON(in_nmi()) in irq_work_queue_on().
At some later stage we could look at maybe merging the smp_function_call and irq_work into a single thing, where we have the irq_work interface as async and the smp_function_call() as sync interface.
But for now a quick 'hack' would be to call __irq_work_run() from generic_smp_call_function_single_interrupt().
That would leave arch_irq_work_raise() as the special NMI safe local IPI hook.
Re: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.
Quoted 5 lines
Quoted 1 line
Signed-off-by Frederic Weisbecker <[email protected]>Why not borrow the
smp_call_functionIPI for the remote bits? We could limit the 'safe from NMI' to the local works. And we validate this by putting a WARN_ON(in_nmi()) inirq_work_queue_on().
Right, but although I don't need it to be safe from NMI, I need it to be callable concurrently and when irqs are disabled.
So we can't use smp_call_function_single() for that. But we can use the async version in which case we must keep the irq work claim. But that's about the same than smp_queue_function_single() we had previously and we are back with our csd_lock issue.
Quoted 10 lines
At some later stage we could look at maybe merging the
smp_function_callandirq_workinto a single thing, where we have theirq_workinterface as async and thesmp_function_call()as sync interface.But for now a quick 'hack' would be to call
__irq_work_run()fromgeneric_smp_call_function_single_interrupt().That would leave
arch_irq_work_raise()as the special NMI safe local IPI hook.
I don't know, calling irq_work_run() from there sounds like an overkill.
Or we can encapsulate:
struct remote_irq_work {
struct irq_work work;
struct call_single_data csd;
}bool irq_work_queue_on(remote_work, cpu)
{
if (irq_work_claim(remote_work.work))
return false;
remote_work.csd.func = irq_work_remote_interrupt; remotr_work.csd.info = work smp_call_function_single_async(&remote_work.csd, cpu);
}
void irq_work_remote_interrupt(void *info)
{
struct irq_work *work = info;
work->func(work);
}And some tweaking to make csd_lock() out of the way. smp_call_function_single_async() don't need it.
Or the two other solutions:
1) expand irq_work to support remote queuing. And really this hasn't added more atomic operations nor smp barriers that the local queuing. The only complication is that we need remote raise support from arch.
2) expand smp_call_function stuff with smp_queue_function_single(). As I did previously. It's the same than the above irq_work_queue_on() above will need to be conditional though.
The native remote support on irq_work sounds to me the most proper. But unfortunately it requires support from arch that we already have with smp_function...
Peter Zijlstra
maintainerRe: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 20 lines · Frederic Weisbecker
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.Quoted 5 lines
Quoted 1 line
Signed-off-by Frederic Weisbecker <[email protected]>Why not borrow the
smp_call_functionIPI for the remote bits? We could limit the 'safe from NMI' to the local works. And we validate this by putting a WARN_ON(in_nmi()) inirq_work_queue_on().
Quoted 7 lines
Right, but although I don't need it to be safe from NMI, I need it to be callable concurrently and when irqs are disabled.
So we can't use
smp_call_function_single()for that. But we can use the async version in which case we must keep the irq work claim. But that's about the same thansmp_queue_function_single()we had previously and we are back with ourcsd_lockissue.
Who said anything about using smp_call_function_single()?
---
kernel/irq_work.c +19 -3
@@ -61,7 +61,8 @@ void __weak arch_irq_work_raise(void)* * Can be re-enqueued while the callback is still in progress. */ -bool irq_work_queue (struct irq_work *work) +static __always_inline bool +__irq_work_queue_on (struct irq_work *work,int cpu) {/* Only queue if not already pending */ if (!irq_work_claim (work))
@@ -78,16 +79,31 @@ bool irq_work_queue(struct irq_work *work)* for the next tick. */ if (!(work->flags &IRQ_WORK_LAZY ) ||tick_nohz_tick_stopped ()) { -if (!this_cpu_cmpxchg (irq_work_raised,0 ,1 )) -arch_irq_work_raise (); +if (cmpxchg (&__get_cpu_var (irq_work_raised,0 ,1 ) ==0 )) { +if (cpu ==smp_processor_id () || cpu ==-1 ) +arch_irq_work_raise (); +else +arch_send_call_function_single_ipi (); + } }preempt_enable ();return true; } + +bool irq_work_queue (struct irq_work *work) +{ +return __irq_work_queue_on (work,-1 ); +}EXPORT_SYMBOL_GPL (irq_work_queue); +bool irq_work_queue_on (struct irq_work *work,int cpu) +{ +WARN_ON_ONCE (in_nmi ()); +return __irq_work_queue_on (work, cpu); +} +bool irq_work_needs_cpu (void ) {struct llist_head *this_list;
kernel/smp.c +6 -0
@@ -198,6 +198,12 @@ void generic_smp_call_function_single_interrupt(void) csd->func (csd->info);csd_unlock (csd); } + +/* +* First run the synchronous callbacks, people are waiting on them; +* then run the async ones. +*/ +irq_work_run (); }/*
Re: [PATCH 1/5] irq_work: Architecture support for remote irq work raise
Quoted 24 lines · Peter Zijlstra
Quoted 23 lines · Frederic Weisbecker
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Frederic Weisbecker
We are going to extend irq work to support remote queuing.
So lets add a cpu argument to
arch_irq_work_raise(). The architectures willing to support that must then provide the backend to raise irq work IPIs remotely.Initial support is provided for x86 and ARM since they are easily extended. The other archs that overwrite
arch_irq_work_raise()seem to use local clock interrupts and therefore need deeper rewrite of their irq work support to implement remote raising.Quoted 5 lines
Quoted 1 line
Signed-off-by Frederic Weisbecker <[email protected]>Why not borrow the
smp_call_functionIPI for the remote bits? We could limit the 'safe from NMI' to the local works. And we validate this by putting a WARN_ON(in_nmi()) inirq_work_queue_on().Right, but although I don't need it to be safe from NMI, I need it to be callable concurrently and when irqs are disabled.
Quoted 6 lines
Quoted 4 lines
So we can't use
smp_call_function_single()for that. But we can use the async version in which case we must keep the irq work claim. But that's about the same thansmp_queue_function_single()we had previously and we are back with ourcsd_lockissue.Who said anything about using
smp_call_function_single()?
Ah shortcutting, doesn't look bad indeed.
Quoted 19 lines
---
kernel/irq_work.c +2
-1@@ -61,7 +61,8 @@ void __weak arch_irq_work_raise(void)* * Can be re-enqueued while the callback is still in progress. */ -bool irq_work_queue (struct irq_work *work) +static __always_inline bool +__irq_work_queue_on (struct irq_work *work,int cpu) {/* Only queue if not already pending */ if (!irq_work_claim (work))@@ -78,16 +79,31 @@ bool irq_work_queue(struct irq_work *work)* for the next tick.
kernel/irq_work.c bool irq_work_queue(struct irq_work *work) · line 87
Quoted 10 lines
kernel/irq_work.c +6
-2*/ if (!(work->flags &IRQ_WORK_LAZY ) ||tick_nohz_tick_stopped ()) { -if (!this_cpu_cmpxchg (irq_work_raised,0 ,1 )) -arch_irq_work_raise (); +if (cmpxchg (&__get_cpu_var (irq_work_raised,0 ,1 ) ==0 )) { +if (cpu ==smp_processor_id () || cpu ==-1 ) +arch_irq_work_raise (); +else +arch_send_call_function_single_ipi (); + }
Ok that needs some more tuning with the raised flag and the destination list to pick, but I get the idea.
Quoted 27 lines
kernel/irq_work.c +11
-0}preempt_enable ();return true; } + +bool irq_work_queue (struct irq_work *work) +{ +return __irq_work_queue_on (work,-1 ); +}EXPORT_SYMBOL_GPL (irq_work_queue); +bool irq_work_queue_on (struct irq_work *work,int cpu) +{ +WARN_ON_ONCE (in_nmi ()); +return __irq_work_queue_on (work, cpu); +} +bool irq_work_needs_cpu (void ) {struct llist_head *this_list;kernel/smp.c +0
-0
kernel/smp.c void generic_smp_call_function_single_interrupt(void) · line 207
Quoted 10 lines
kernel/smp.c +6
-0@@ -198,6 +198,12 @@ void generic_smp_call_function_single_interrupt(void) csd->func (csd->info);csd_unlock (csd); } + +/* +* First run the synchronous callbacks, people are waiting on them; +* then run the async ones. +*/ +irq_work_run (); }
Alright, I'm reiterating with that.
Thanks.
PATCH 2/5 irq_work: Force non-lazy works on IPI
In reply to Frederic WeisbeckerAs we plan to handle the full nohz IPI using irq work, we need to enforce non-lazy works outside the tick because it's called under hrtimer lock. This is not desired from the nohz callback revaluating the tick because it can take hrtimer lock itself.
--- include/linux/irq_work.h | 1 + kernel/irq_work.c | 58 ++++++++++++++++++++++++++---------------------- kernel/timer.c | 2 +- 3 files changed, 34 insertions(+), 27 deletions(-)
include/linux/irq_work.h +1 -0
@@ -34,6 +34,7 @@ void init_irq_work(struct irq_work *work, void (*func)(struct irq_work *))bool irq_work_queue (struct irq_work *work);void irq_work_run (void ); +void irq_work_run_tick (void );void irq_work_sync (struct irq_work *work);#ifdef CONFIG_IRQ_WORK
kernel/irq_work.c +32 -26
121 lines not on this page · raw message
kernel/timer.c +1 -1
@@ -1384,7 +1384,7 @@ void update_process_times(int user_tick)rcu_check_callbacks (cpu, user_tick);#ifdef CONFIG_IRQ_WORK if (in_irq ()) -irq_work_run (); +irq_work_run_tick ();#endif scheduler_tick ();run_posix_cpu_timers (p);
Signature · 1 line
1.8.3.1
PATCH 3/5 irq_work: Allow remote queueing
In reply to Frederic Weisbeckerirq work currently only supports local callbacks. However its code is mostly ready to run remote callbacks and we have some potential user.
The full nohz subsystem currently open codes its own remote irq work on top of the scheduler ipi when it wants a CPU to revaluate its next tick.
However this ad hoc solution bloats the scheduler IPI.
Lets just extend the irq work subsystem to handle this kind of user. This shouldn't add noticeable overhead.
--- include/linux/irq_work.h | 2 ++ kernel/irq_work.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-)
include/linux/irq_work.h +2 -0
@@ -33,6 +33,8 @@ void init_irq_work(struct irq_work *work, void (*func)(struct irq_work *))#define DEFINE_IRQ_WORK (name, _f) struct irq_work name = { .func = (_f), }bool irq_work_queue (struct irq_work *work); +bool irq_work_queue_on (struct irq_work *work,int cpu); +void irq_work_run (void );void irq_work_run_tick (void );void irq_work_sync (struct irq_work *work);
kernel/irq_work.c +27 -2
@@ -55,12 +55,37 @@ void __weak arch_irq_work_raise(int cpu)*/ } +static void irq_work_queue_raise (struct irq_work *work, +struct llist_head *head,int cpu) +{ +if (llist_add (&work->llnode, head)) +arch_irq_work_raise (cpu); +} + +#ifdef CONFIG_HAVE_IRQ_WORK_IPI /* * Enqueue the irq_work @entry unless it's already pending * somewhere. * * Can be re-enqueued while the callback is still in progress. */ +bool irq_work_queue_on (struct irq_work *work,int cpu) +{ +/* Only queue if not already pending */ +if (!irq_work_claim (work)) +return false; + +/* All work should have been flushed before going offline */ +WARN_ON_ONCE (cpu_is_offline (cpu)); +WARN_ON_ONCE (work->flags &IRQ_WORK_LAZY ); + +irq_work_queue_raise (work, &per_cpu (raised_list, cpu), cpu); + +return true; +} +EXPORT_SYMBOL_GPL (irq_work_queue_on); +#endif /* #endif CONFIG_HAVE_IRQ_WORK_IPI */ +bool irq_work_queue (struct irq_work *work) {unsigned long flags;
@@ -78,8 +103,8 @@ bool irq_work_queue(struct irq_work *work)* for the next tick. */ if (!(work->flags &IRQ_WORK_LAZY ) ||tick_nohz_tick_stopped ()) { -if (llist_add (&work->llnode, &__get_cpu_var (raised_list))) -arch_irq_work_raise (smp_processor_id ()); +irq_work_queue_raise (work, &__get_cpu_var (raised_list), +smp_processor_id ()); }else {llist_add (&work->llnode, &__get_cpu_var (lazy_list)); }
Signature · 1 line
1.8.3.1
PATCH 4/5 nohz: Move full nohz kick to its own IPI
In reply to Frederic WeisbeckerNow that the irq work subsystem can queue remote callbacks, it's a perfect fit to safely queue IPIs when interrupts are disabled without worrying about concurrent callers.
Lets use it for the full dynticks kick to notify a CPU that it's exiting single task mode.
This unbloats a bit the scheduler IPI that the nohz code was abusing for its cool "callable anywhere/anytime" properties.
--- include/linux/tick.h | 9 ++++++++- kernel/sched/core.c | 5 +---- kernel/sched/sched.h | 2 +- kernel/time/Kconfig | 2 ++ kernel/time/tick-sched.c | 10 ++++++---- 5 files changed, 18 insertions(+), 10 deletions(-)
include/linux/tick.h +8 -1
@@ -181,7 +181,13 @@ static inline bool tick_nohz_full_cpu(int cpu)extern void tick_nohz_init (void );extern void __tick_nohz_full_check (void ); -extern void tick_nohz_full_kick (void ); +extern void tick_nohz_full_kick_cpu (int cpu); + +static inline void tick_nohz_full_kick (void ) +{ +tick_nohz_full_kick_cpu (smp_processor_id ()); +} +extern void tick_nohz_full_kick_all (void );extern void __tick_nohz_task_switch (struct task_struct *tsk);#else
@@ -189,6 +195,7 @@ static inline void tick_nohz_init(void) { }static inline bool tick_nohz_full_enabled (void ) {return false; }static inline bool tick_nohz_full_cpu (int cpu) {return false; }static inline void __tick_nohz_full_check (void ) { } +static inline void tick_nohz_full_kick_cpu (int cpu) { }static inline void tick_nohz_full_kick (void ) { }static inline void tick_nohz_full_kick_all (void ) { }static inline void __tick_nohz_task_switch (struct task_struct *tsk) { }
kernel/sched/core.c +1 -4
@@ -1500,9 +1500,7 @@ void scheduler_ipi(void)*/ preempt_fold_need_resched (); -if (llist_empty (&this_rq ()->wake_list) - && !tick_nohz_full_cpu (smp_processor_id ()) - && !got_nohz_idle_kick ()) +if (llist_empty (&this_rq ()->wake_list) && !got_nohz_idle_kick ())return ;/*
@@ -1519,7 +1517,6 @@ void scheduler_ipi(void)* somewhat pessimize the simple resched case. */ irq_enter (); -tick_nohz_full_check ();sched_ttwu_pending ();/*
kernel/sched/sched.h +1 -1
@@ -1225,7 +1225,7 @@ static inline void inc_nr_running(struct rq *rq)if (tick_nohz_full_cpu (rq->cpu)) {/* Order rq->nr_running write against the IPI */ smp_wmb (); -smp_send_reschedule (rq->cpu); +tick_nohz_full_kick_cpu (rq->cpu); } }#endif
kernel/time/Kconfig +2 -0
@@ -101,6 +101,8 @@ config NO_HZ_FULL depends on HAVE_CONTEXT_TRACKING # VIRT_CPU_ACCOUNTING_GEN dependency depends on HAVE_VIRT_CPU_ACCOUNTING_GEN + # tickless irq work + depends on HAVE_IRQ_WORK_IPI select NO_HZ_COMMON select RCU_USER_QS select RCU_NOCB_CPU
kernel/time/tick-sched.c +6 -4
@@ -224,13 +224,15 @@ static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { };/* -* Kick the current CPU if it's full dynticks in order to force it to +* Kick the CPU if it's full dynticks in order to force it to * re-evaluate its dependency on the tick and restart it if necessary. */ -void tick_nohz_full_kick (void ) +void tick_nohz_full_kick_cpu (int cpu) { -if (tick_nohz_full_cpu (smp_processor_id ())) -irq_work_queue (&__get_cpu_var (nohz_full_kick_work)); +if (!tick_nohz_full_cpu (cpu)) +return ; + +irq_work_queue_on (&per_cpu (nohz_full_kick_work, cpu), cpu); }static void nohz_full_kick_ipi (void *info)
Signature · 1 line
1.8.3.1
PATCH 5/5 nohz: Use IPI implicit full barrier against rq->nr_running r/w
In reply to Frederic WeisbeckerA full dynticks CPU is allowed to stop its tick when a single task runs. Meanwhile when a new task gets enqueued, the CPU must be notified so that it restart its tick to maintain local fairness and other accounting details.
This notification is performed by way of an IPI. Then when the target receives the IPI, we expect it to see the new value of rq->nr_running.
Hence the following ordering scenario:
CPU 0 CPU 1
write rq->running get IPI smp_wmb() smp_rmb() send IPI read rq->nr_running
But Paul Mckenney says that nowadays IPIs imply a full barrier on all architectures. So we can safely remove this pair and rely on the implicit barriers that come along IPI send/receive. Lets just comment on this new assumption.
--- kernel/sched/core.c | 9 +++++---- kernel/sched/sched.h | 10 ++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-)
kernel/sched/core.c +5 -4
@@ -670,10 +670,11 @@ bool sched_can_stop_tick(void) rq =this_rq (); -/* Make sure rq->nr_running update is visible after the IPI */ -smp_rmb (); - -/* More than one running task need preemption */ +/* +* More than one running task need preemption. +* nr_running update is assumed to be visible +* after IPI is sent from wakers. +*/ if (rq->nr_running >1 )return false;
kernel/sched/sched.h +8 -2
@@ -1223,8 +1223,14 @@ static inline void inc_nr_running(struct rq *rq)#ifdef CONFIG_NO_HZ_FULL if (rq->nr_running ==2 ) {if (tick_nohz_full_cpu (rq->cpu)) { -/* Order rq->nr_running write against the IPI */ -smp_wmb (); +/* +* Tick is needed if more than one task runs on a CPU. +* Send the target an IPI to kick it out of nohz mode. +* +* We assume that IPI implies full memory barrier and the +* new value of rq->nr_running is visible on reception +* from the target. +*/ tick_nohz_full_kick_cpu (rq->cpu); } }
Signature · 1 line
1.8.3.1