diff options
author | Milton Miller <miltonm@bga.com> | 2011-05-10 15:29:46 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-05-19 01:31:31 -0400 |
commit | 714542721b4a53a3ebbdd5f0619ac0f66e7df610 (patch) | |
tree | 50f79e4a44c0fe056e2a69e6347e7c8ae2722eff /arch/powerpc/kernel/smp.c | |
parent | 1ece355b6825b7c61d1dc39a5c6cf49dc746e193 (diff) |
powerpc: Use bytes instead of bitops in smp ipi multiplexing
Since there are only 4 messages, we can replace the atomic bit set
(which uses atomic load reserve and store conditional sequence) with
a byte stores to seperate bytes. We still have to perform a load
reserve and store conditional sequence to avoid loosing messages on
reception but we can do that with a single call to xchg.
The do {} while and __BIG_ENDIAN specific mask testing was chosen by
looking at the generated asm code. On gcc-4.4, the bit masking becomes
a simple bit mask and test of the register returned from xchg without
storing and loading the value to the stack like attempts with a union
of bytes and an int (or worse, loading single bit constants from the
constant pool into non-voliatle registers that had to be preseved on
the stack). The do {} while avoids an unconditional branch to the
end of the loop to test the entry / repeat condition of a while loop
and instead optimises for the expected single iteration of the loop.
We have a full mb() at the beginning to cover ordering between send,
ipi, and receive so we can use xchg_local and forgo the further
acquire and release barriers of xchg.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/smp.c')
-rw-r--r-- | arch/powerpc/kernel/smp.c | 31 |
1 files changed, 18 insertions, 13 deletions
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index d76f7d7929be..a8909aa50642 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c | |||
@@ -180,7 +180,7 @@ int smp_request_message_ipi(int virq, int msg) | |||
180 | 180 | ||
181 | #ifdef CONFIG_PPC_SMP_MUXED_IPI | 181 | #ifdef CONFIG_PPC_SMP_MUXED_IPI |
182 | struct cpu_messages { | 182 | struct cpu_messages { |
183 | unsigned long messages; /* current messages bits */ | 183 | int messages; /* current messages */ |
184 | unsigned long data; /* data for cause ipi */ | 184 | unsigned long data; /* data for cause ipi */ |
185 | }; | 185 | }; |
186 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_messages, ipi_message); | 186 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_messages, ipi_message); |
@@ -195,9 +195,9 @@ void smp_muxed_ipi_set_data(int cpu, unsigned long data) | |||
195 | void smp_muxed_ipi_message_pass(int cpu, int msg) | 195 | void smp_muxed_ipi_message_pass(int cpu, int msg) |
196 | { | 196 | { |
197 | struct cpu_messages *info = &per_cpu(ipi_message, cpu); | 197 | struct cpu_messages *info = &per_cpu(ipi_message, cpu); |
198 | unsigned long *tgt = &info->messages; | 198 | char *message = (char *)&info->messages; |
199 | 199 | ||
200 | set_bit(msg, tgt); | 200 | message[msg] = 1; |
201 | mb(); | 201 | mb(); |
202 | smp_ops->cause_ipi(cpu, info->data); | 202 | smp_ops->cause_ipi(cpu, info->data); |
203 | } | 203 | } |
@@ -205,30 +205,35 @@ void smp_muxed_ipi_message_pass(int cpu, int msg) | |||
205 | void smp_muxed_ipi_resend(void) | 205 | void smp_muxed_ipi_resend(void) |
206 | { | 206 | { |
207 | struct cpu_messages *info = &__get_cpu_var(ipi_message); | 207 | struct cpu_messages *info = &__get_cpu_var(ipi_message); |
208 | unsigned long *tgt = &info->messages; | ||
209 | 208 | ||
210 | if (*tgt) | 209 | if (info->messages) |
211 | smp_ops->cause_ipi(smp_processor_id(), info->data); | 210 | smp_ops->cause_ipi(smp_processor_id(), info->data); |
212 | } | 211 | } |
213 | 212 | ||
214 | irqreturn_t smp_ipi_demux(void) | 213 | irqreturn_t smp_ipi_demux(void) |
215 | { | 214 | { |
216 | struct cpu_messages *info = &__get_cpu_var(ipi_message); | 215 | struct cpu_messages *info = &__get_cpu_var(ipi_message); |
217 | unsigned long *tgt = &info->messages; | 216 | unsigned int all; |
218 | 217 | ||
219 | mb(); /* order any irq clear */ | 218 | mb(); /* order any irq clear */ |
220 | while (*tgt) { | 219 | |
221 | if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, tgt)) | 220 | do { |
221 | all = xchg_local(&info->messages, 0); | ||
222 | |||
223 | #ifdef __BIG_ENDIAN | ||
224 | if (all & (1 << (24 - 8 * PPC_MSG_CALL_FUNCTION))) | ||
222 | generic_smp_call_function_interrupt(); | 225 | generic_smp_call_function_interrupt(); |
223 | if (test_and_clear_bit(PPC_MSG_RESCHEDULE, tgt)) | 226 | if (all & (1 << (24 - 8 * PPC_MSG_RESCHEDULE))) |
224 | reschedule_action(0, NULL); /* upcoming sched hook */ | 227 | reschedule_action(0, NULL); /* upcoming sched hook */ |
225 | if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE, tgt)) | 228 | if (all & (1 << (24 - 8 * PPC_MSG_CALL_FUNC_SINGLE))) |
226 | generic_smp_call_function_single_interrupt(); | 229 | generic_smp_call_function_single_interrupt(); |
227 | #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) | 230 | if (all & (1 << (24 - 8 * PPC_MSG_DEBUGGER_BREAK))) |
228 | if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK, tgt)) | ||
229 | debug_ipi_action(0, NULL); | 231 | debug_ipi_action(0, NULL); |
232 | #else | ||
233 | #error Unsupported ENDIAN | ||
230 | #endif | 234 | #endif |
231 | } | 235 | } while (info->messages); |
236 | |||
232 | return IRQ_HANDLED; | 237 | return IRQ_HANDLED; |
233 | } | 238 | } |
234 | #endif /* CONFIG_PPC_SMP_MUXED_IPI */ | 239 | #endif /* CONFIG_PPC_SMP_MUXED_IPI */ |