Skip to content

Commit

Permalink
irq_work: Implement remote queueing
Browse files Browse the repository at this point in the history
irq 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 reevaluate its next
tick. However this ad hoc solution bloats the scheduler IPI.

Lets just extend the irq work subsystem to support remote queuing on top
of the generic SMP IPI to handle this kind of user. This shouldn't add
noticeable overhead.

Suggested-by: Peter Zijlstra <[email protected]>
Acked-by: Peter Zijlstra <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Eric Dumazet <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Kevin Hilman <[email protected]>
Cc: Paul E. McKenney <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Viresh Kumar <[email protected]>
Signed-off-by: Frederic Weisbecker <[email protected]>
  • Loading branch information
fweisbec committed Jun 16, 2014
1 parent b93e0b8 commit 4788501
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 1 deletion.
5 changes: 5 additions & 0 deletions include/linux/irq_work.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ 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);

#ifdef CONFIG_SMP
bool irq_work_queue_on(struct irq_work *work, int cpu);
#endif

void irq_work_run(void);
void irq_work_sync(struct irq_work *work);

Expand Down
25 changes: 24 additions & 1 deletion kernel/irq_work.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/tick.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/smp.h>
#include <asm/processor.h>


Expand Down Expand Up @@ -55,12 +56,34 @@ void __weak arch_irq_work_raise(void)
*/
}

#ifdef CONFIG_SMP
/*
* Enqueue the irq_work @entry unless it's already pending
* Enqueue the irq_work @work on @cpu 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)
{
/* All work should have been flushed before going offline */
WARN_ON_ONCE(cpu_is_offline(cpu));

/* Arch remote IPI send/receive backend aren't NMI safe */
WARN_ON_ONCE(in_nmi());

/* Only queue if not already pending */
if (!irq_work_claim(work))
return false;

if (llist_add(&work->llnode, &per_cpu(raised_list, cpu)))
arch_send_call_function_single_ipi(cpu);

return true;
}
EXPORT_SYMBOL_GPL(irq_work_queue_on);
#endif

/* Enqueue the irq work @work on the current CPU */
bool irq_work_queue(struct irq_work *work)
{
/* Only queue if not already pending */
Expand Down
9 changes: 9 additions & 0 deletions kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* (C) Jens Axboe <[email protected]> 2008
*/
#include <linux/irq_work.h>
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <linux/kernel.h>
Expand Down Expand Up @@ -210,6 +211,14 @@ void generic_smp_call_function_single_interrupt(void)
csd->func(csd->info);
csd_unlock(csd);
}

/*
* Handle irq works queued remotely by irq_work_queue_on().
* Smp functions above are typically synchronous so they
* better run first since some other CPUs may be busy waiting
* for them.
*/
irq_work_run();
}

/*
Expand Down

0 comments on commit 4788501

Please sign in to comment.