aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-07-09 01:29:53 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-07-09 01:29:53 -0400
commitb9f1cd71dbf21a91fb7e2336a1d1ff18b97771e5 (patch)
tree3c99af81977a06bce82f13ab1f3c748a444c5362
parente3145b387a02d4bf8b8033b1354d413fc0864494 (diff)
powerpc/book3e: More doorbell cleanups. Sample the PIR register
The doorbells use the content of the PIR register to match messages from other CPUs. This may or may not be the same as our linux CPU number, so using that as the "target" is no right. Instead, we sample the PIR register at boot on every processor and use that value subsequently when sending IPIs. We also use a per-cpu message mask rather than a global array which should limit cache line contention. Note: We could use the CPU number in the device-tree instead of the PIR register, as they are supposed to be equivalent. This might prove useful if doorbells are to be used to kick CPUs out of FW at boot time, thus before we can sample the PIR. This is however not the case now and using the PIR just works. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/dbell.h7
-rw-r--r--arch/powerpc/kernel/dbell.c47
-rw-r--r--arch/powerpc/platforms/85xx/smp.c4
3 files changed, 40 insertions, 18 deletions
diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h
index 501189a543d1..ced7e487ca27 100644
--- a/arch/powerpc/include/asm/dbell.h
+++ b/arch/powerpc/include/asm/dbell.h
@@ -27,10 +27,9 @@ enum ppc_dbell {
27 PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */ 27 PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */
28}; 28};
29 29
30#ifdef CONFIG_SMP 30extern void doorbell_message_pass(int target, int msg);
31extern unsigned long dbell_smp_message[NR_CPUS]; 31extern void doorbell_exception(struct pt_regs *regs);
32extern void smp_dbell_message_pass(int target, int msg); 32extern void doorbell_setup_this_cpu(void);
33#endif
34 33
35static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag) 34static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag)
36{ 35{
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index e3a717704fd6..1c7a94580c3f 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -13,45 +13,66 @@
13#include <linux/kernel.h> 13#include <linux/kernel.h>
14#include <linux/smp.h> 14#include <linux/smp.h>
15#include <linux/threads.h> 15#include <linux/threads.h>
16#include <linux/percpu.h>
16 17
17#include <asm/dbell.h> 18#include <asm/dbell.h>
18 19
19#ifdef CONFIG_SMP 20#ifdef CONFIG_SMP
20unsigned long dbell_smp_message[NR_CPUS]; 21struct doorbell_cpu_info {
22 unsigned long messages; /* current messages bits */
23 unsigned int tag; /* tag value */
24};
21 25
22void smp_dbell_message_pass(int target, int msg) 26static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info);
27
28void doorbell_setup_this_cpu(void)
29{
30 struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
31
32 info->messages = 0;
33 info->tag = mfspr(SPRN_PIR) & 0x3fff;
34}
35
36void doorbell_message_pass(int target, int msg)
23{ 37{
38 struct doorbell_cpu_info *info;
24 int i; 39 int i;
25 40
26 if(target < NR_CPUS) { 41 if (target < NR_CPUS) {
27 set_bit(msg, &dbell_smp_message[target]); 42 info = &per_cpu(doorbell_cpu_info, target);
28 ppc_msgsnd(PPC_DBELL, 0, target); 43 set_bit(msg, &info->messages);
44 ppc_msgsnd(PPC_DBELL, 0, info->tag);
29 } 45 }
30 else if(target == MSG_ALL_BUT_SELF) { 46 else if (target == MSG_ALL_BUT_SELF) {
31 for_each_online_cpu(i) { 47 for_each_online_cpu(i) {
32 if (i == smp_processor_id()) 48 if (i == smp_processor_id())
33 continue; 49 continue;
34 set_bit(msg, &dbell_smp_message[i]); 50 info = &per_cpu(doorbell_cpu_info, i);
35 ppc_msgsnd(PPC_DBELL, 0, i); 51 set_bit(msg, &info->messages);
52 ppc_msgsnd(PPC_DBELL, 0, info->tag);
36 } 53 }
37 } 54 }
38 else { /* target == MSG_ALL */ 55 else { /* target == MSG_ALL */
39 for_each_online_cpu(i) 56 for_each_online_cpu(i) {
40 set_bit(msg, &dbell_smp_message[i]); 57 info = &per_cpu(doorbell_cpu_info, i);
58 set_bit(msg, &info->messages);
59 }
41 ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0); 60 ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0);
42 } 61 }
43} 62}
44 63
45void doorbell_exception(struct pt_regs *regs) 64void doorbell_exception(struct pt_regs *regs)
46{ 65{
47 int cpu = smp_processor_id(); 66 struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
48 int msg; 67 int msg;
49 68
50 if (num_online_cpus() < 2) 69 /* Warning: regs can be NULL when called from irq enable */
70
71 if (!info->messages || (num_online_cpus() < 2))
51 return; 72 return;
52 73
53 for (msg = 0; msg < 4; msg++) 74 for (msg = 0; msg < 4; msg++)
54 if (test_and_clear_bit(msg, &dbell_smp_message[cpu])) 75 if (test_and_clear_bit(msg, &info->messages))
55 smp_message_recv(msg); 76 smp_message_recv(msg);
56} 77}
57 78
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index a15f582300d8..4c3cde911c71 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -99,6 +99,8 @@ static void __init
99smp_85xx_setup_cpu(int cpu_nr) 99smp_85xx_setup_cpu(int cpu_nr)
100{ 100{
101 mpic_setup_this_cpu(); 101 mpic_setup_this_cpu();
102 if (cpu_has_feature(CPU_FTR_DBELL))
103 doorbell_setup_this_cpu();
102} 104}
103 105
104struct smp_ops_t smp_85xx_ops = { 106struct smp_ops_t smp_85xx_ops = {
@@ -117,7 +119,7 @@ void __init mpc85xx_smp_init(void)
117 } 119 }
118 120
119 if (cpu_has_feature(CPU_FTR_DBELL)) 121 if (cpu_has_feature(CPU_FTR_DBELL))
120 smp_85xx_ops.message_pass = smp_dbell_message_pass; 122 smp_85xx_ops.message_pass = doorbell_message_pass;
121 123
122 BUG_ON(!smp_85xx_ops.message_pass); 124 BUG_ON(!smp_85xx_ops.message_pass);
123 125