Commit d8e8c7dd authored by Vineet Gupta's avatar Vineet Gupta

ARC: [SMP] optimize IPI send and receive

* Don't send an IPI if receiver already has a pending IPI.
  Atomically piggyback the new msg with pending msg.

* IPI receiver looping on xchg() not required

References: https://lkml.org/lkml/2013/11/25/232Suggested-by: default avatarPeter Zijlstra <peterz@infradead.org>
Signed-off-by: default avatarVineet Gupta <vgupta@synopsys.com>
parent f2a4aa56
...@@ -215,16 +215,31 @@ static DEFINE_PER_CPU(unsigned long, ipi_data); ...@@ -215,16 +215,31 @@ static DEFINE_PER_CPU(unsigned long, ipi_data);
static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg) static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)
{ {
unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu);
unsigned long old, new;
unsigned long flags; unsigned long flags;
pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu); pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);
local_irq_save(flags); local_irq_save(flags);
set_bit(msg, ipi_data_ptr); /*
* Atomically write new msg bit (in case others are writing too),
* and read back old value
*/
do {
new = old = *ipi_data_ptr;
new |= 1U << msg;
} while (cmpxchg(ipi_data_ptr, old, new) != old);
/* Call the platform specific cross-CPU call function */ /*
if (plat_smp_ops.ipi_send) * Call the platform specific IPI kick function, but avoid if possible:
* Only do so if there's no pending msg from other concurrent sender(s).
* Otherwise, recevier will see this msg as well when it takes the
* IPI corresponding to that msg. This is true, even if it is already in
* IPI handler, because !@old means it has not yet dequeued the msg(s)
* so @new msg can be a free-loader
*/
if (plat_smp_ops.ipi_send && !old)
plat_smp_ops.ipi_send(cpu); plat_smp_ops.ipi_send(cpu);
local_irq_restore(flags); local_irq_restore(flags);
...@@ -269,12 +284,8 @@ static void ipi_cpu_stop(void) ...@@ -269,12 +284,8 @@ static void ipi_cpu_stop(void)
machine_halt(); machine_halt();
} }
static inline void __do_IPI(unsigned long pending) static inline void __do_IPI(unsigned long msg)
{ {
while (pending) {
unsigned long msg = __ffs(pending);
switch (msg) { switch (msg) {
case IPI_RESCHEDULE: case IPI_RESCHEDULE:
scheduler_ipi(); scheduler_ipi();
...@@ -289,11 +300,7 @@ static inline void __do_IPI(unsigned long pending) ...@@ -289,11 +300,7 @@ static inline void __do_IPI(unsigned long pending)
break; break;
default: default:
pr_warn("IPI missing msg\n"); pr_warn("IPI with unexpected msg %ld\n", msg);
}
pending &= ~(1U << msg);
} }
} }
...@@ -312,11 +319,16 @@ irqreturn_t do_IPI(int irq, void *dev_id) ...@@ -312,11 +319,16 @@ irqreturn_t do_IPI(int irq, void *dev_id)
plat_smp_ops.ipi_clear(irq); plat_smp_ops.ipi_clear(irq);
/* /*
* XXX: is this loop really needed * "dequeue" the msg corresponding to this IPI (and possibly other
* And do we need to move ipi_clean inside * piggybacked msg from elided IPIs: see ipi_send_msg_one() above)
*/ */
while ((pending = xchg(this_cpu_ptr(&ipi_data), 0)) != 0) pending = xchg(this_cpu_ptr(&ipi_data), 0);
__do_IPI(pending);
do {
unsigned long msg = __ffs(pending);
__do_IPI(msg);
pending &= ~(1U << msg);
} while (pending);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment