Commit d8e8c7dda11f5d5cf90495f2e89d917a83509bc0

Authored by Vineet Gupta
1 parent f2a4aa5646

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/232
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

Showing 1 changed file with 40 additions and 28 deletions Side-by-side Diff

arch/arc/kernel/smp.c
... ... @@ -215,16 +215,31 @@
215 215 static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)
216 216 {
217 217 unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu);
  218 + unsigned long old, new;
218 219 unsigned long flags;
219 220  
220 221 pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);
221 222  
222 223 local_irq_save(flags);
223 224  
224   - set_bit(msg, ipi_data_ptr);
  225 + /*
  226 + * Atomically write new msg bit (in case others are writing too),
  227 + * and read back old value
  228 + */
  229 + do {
  230 + new = old = *ipi_data_ptr;
  231 + new |= 1U << msg;
  232 + } while (cmpxchg(ipi_data_ptr, old, new) != old);
225 233  
226   - /* Call the platform specific cross-CPU call function */
227   - if (plat_smp_ops.ipi_send)
  234 + /*
  235 + * Call the platform specific IPI kick function, but avoid if possible:
  236 + * Only do so if there's no pending msg from other concurrent sender(s).
  237 + * Otherwise, recevier will see this msg as well when it takes the
  238 + * IPI corresponding to that msg. This is true, even if it is already in
  239 + * IPI handler, because !@old means it has not yet dequeued the msg(s)
  240 + * so @new msg can be a free-loader
  241 + */
  242 + if (plat_smp_ops.ipi_send && !old)
228 243 plat_smp_ops.ipi_send(cpu);
229 244  
230 245 local_irq_restore(flags);
231 246  
232 247  
233 248  
234 249  
... ... @@ -269,31 +284,23 @@
269 284 machine_halt();
270 285 }
271 286  
272   -static inline void __do_IPI(unsigned long pending)
  287 +static inline void __do_IPI(unsigned long msg)
273 288 {
274   - while (pending) {
  289 + switch (msg) {
  290 + case IPI_RESCHEDULE:
  291 + scheduler_ipi();
  292 + break;
275 293  
276   - unsigned long msg = __ffs(pending);
  294 + case IPI_CALL_FUNC:
  295 + generic_smp_call_function_interrupt();
  296 + break;
277 297  
278   - switch (msg) {
279   - case IPI_RESCHEDULE:
280   - scheduler_ipi();
281   - break;
  298 + case IPI_CPU_STOP:
  299 + ipi_cpu_stop();
  300 + break;
282 301  
283   - case IPI_CALL_FUNC:
284   - generic_smp_call_function_interrupt();
285   - break;
286   -
287   - case IPI_CPU_STOP:
288   - ipi_cpu_stop();
289   - break;
290   -
291   - default:
292   - pr_warn("IPI missing msg\n");
293   -
294   - }
295   -
296   - pending &= ~(1U << msg);
  302 + default:
  303 + pr_warn("IPI with unexpected msg %ld\n", msg);
297 304 }
298 305 }
299 306  
300 307  
... ... @@ -312,11 +319,16 @@
312 319 plat_smp_ops.ipi_clear(irq);
313 320  
314 321 /*
315   - * XXX: is this loop really needed
316   - * And do we need to move ipi_clean inside
  322 + * "dequeue" the msg corresponding to this IPI (and possibly other
  323 + * piggybacked msg from elided IPIs: see ipi_send_msg_one() above)
317 324 */
318   - while ((pending = xchg(this_cpu_ptr(&ipi_data), 0)) != 0)
319   - __do_IPI(pending);
  325 + pending = xchg(this_cpu_ptr(&ipi_data), 0);
  326 +
  327 + do {
  328 + unsigned long msg = __ffs(pending);
  329 + __do_IPI(msg);
  330 + pending &= ~(1U << msg);
  331 + } while (pending);
320 332  
321 333 return IRQ_HANDLED;
322 334 }