diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-11-28 03:27:54 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-12-23 01:35:04 -0500 |
commit | d8e8c7dda11f5d5cf90495f2e89d917a83509bc0 (patch) | |
tree | 8b8414e2bd4ed6b92bf35a36a3d891fa14399661 /arch/arc | |
parent | f2a4aa5646687f9a51616581221b1d348590d47e (diff) |
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>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/kernel/smp.c | 68 |
1 files changed, 40 insertions, 28 deletions
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index c00c612e8dd3..40859e5619f9 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c | |||
@@ -215,16 +215,31 @@ static DEFINE_PER_CPU(unsigned long, ipi_data); | |||
215 | static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg) | 215 | static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg) |
216 | { | 216 | { |
217 | unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); | 217 | unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); |
218 | unsigned long old, new; | ||
218 | unsigned long flags; | 219 | unsigned long flags; |
219 | 220 | ||
220 | pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu); | 221 | pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu); |
221 | 222 | ||
222 | local_irq_save(flags); | 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 */ | 234 | /* |
227 | if (plat_smp_ops.ipi_send) | 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 | plat_smp_ops.ipi_send(cpu); | 243 | plat_smp_ops.ipi_send(cpu); |
229 | 244 | ||
230 | local_irq_restore(flags); | 245 | local_irq_restore(flags); |
@@ -269,31 +284,23 @@ static void ipi_cpu_stop(void) | |||
269 | machine_halt(); | 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) { |
275 | 290 | case IPI_RESCHEDULE: | |
276 | unsigned long msg = __ffs(pending); | 291 | scheduler_ipi(); |
277 | 292 | break; | |
278 | switch (msg) { | ||
279 | case IPI_RESCHEDULE: | ||
280 | scheduler_ipi(); | ||
281 | break; | ||
282 | 293 | ||
283 | case IPI_CALL_FUNC: | 294 | case IPI_CALL_FUNC: |
284 | generic_smp_call_function_interrupt(); | 295 | generic_smp_call_function_interrupt(); |
285 | break; | 296 | break; |
286 | |||
287 | case IPI_CPU_STOP: | ||
288 | ipi_cpu_stop(); | ||
289 | break; | ||
290 | |||
291 | default: | ||
292 | pr_warn("IPI missing msg\n"); | ||
293 | 297 | ||
294 | } | 298 | case IPI_CPU_STOP: |
299 | ipi_cpu_stop(); | ||
300 | break; | ||
295 | 301 | ||
296 | pending &= ~(1U << msg); | 302 | default: |
303 | pr_warn("IPI with unexpected msg %ld\n", msg); | ||
297 | } | 304 | } |
298 | } | 305 | } |
299 | 306 | ||
@@ -312,11 +319,16 @@ irqreturn_t do_IPI(int irq, void *dev_id) | |||
312 | plat_smp_ops.ipi_clear(irq); | 319 | plat_smp_ops.ipi_clear(irq); |
313 | 320 | ||
314 | /* | 321 | /* |
315 | * XXX: is this loop really needed | 322 | * "dequeue" the msg corresponding to this IPI (and possibly other |
316 | * And do we need to move ipi_clean inside | 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) | 325 | pending = xchg(this_cpu_ptr(&ipi_data), 0); |
319 | __do_IPI(pending); | 326 | |
327 | do { | ||
328 | unsigned long msg = __ffs(pending); | ||
329 | __do_IPI(msg); | ||
330 | pending &= ~(1U << msg); | ||
331 | } while (pending); | ||
320 | 332 | ||
321 | return IRQ_HANDLED; | 333 | return IRQ_HANDLED; |
322 | } | 334 | } |