]> git.karo-electronics.de Git - linux-beck.git/commitdiff
powerpc/book3e: More doorbell cleanups. Sample the PIR register
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 9 Jul 2010 05:29:53 +0000 (15:29 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 9 Jul 2010 05:29:53 +0000 (15:29 +1000)
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>
arch/powerpc/include/asm/dbell.h
arch/powerpc/kernel/dbell.c
arch/powerpc/platforms/85xx/smp.c

index 501189a543d1366e42deb5e98a12ca5a9959b2c7..ced7e487ca273590991d2665fe8fcee29fcf6090 100644 (file)
@@ -27,10 +27,9 @@ enum ppc_dbell {
        PPC_G_DBELL_MC = 4,     /* guest mcheck doorbell */
 };
 
-#ifdef CONFIG_SMP
-extern unsigned long dbell_smp_message[NR_CPUS];
-extern void smp_dbell_message_pass(int target, int msg);
-#endif
+extern void doorbell_message_pass(int target, int msg);
+extern void doorbell_exception(struct pt_regs *regs);
+extern void doorbell_setup_this_cpu(void);
 
 static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag)
 {
index e3a717704fd6cad7ac1817bca7d15538fd6e7106..1c7a94580c3f78b6d574f8c1038a179d69feb278 100644 (file)
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/threads.h>
+#include <linux/percpu.h>
 
 #include <asm/dbell.h>
 
 #ifdef CONFIG_SMP
-unsigned long dbell_smp_message[NR_CPUS];
+struct doorbell_cpu_info {
+       unsigned long   messages;       /* current messages bits */
+       unsigned int    tag;            /* tag value */
+};
 
-void smp_dbell_message_pass(int target, int msg)
+static DEFINE_PER_CPU(struct doorbell_cpu_info, doorbell_cpu_info);
+
+void doorbell_setup_this_cpu(void)
+{
+       struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
+
+       info->messages = 0;
+       info->tag = mfspr(SPRN_PIR) & 0x3fff;
+}
+
+void doorbell_message_pass(int target, int msg)
 {
+       struct doorbell_cpu_info *info;
        int i;
 
-       if(target < NR_CPUS) {
-               set_bit(msg, &dbell_smp_message[target]);
-               ppc_msgsnd(PPC_DBELL, 0, target);
+       if (target < NR_CPUS) {
+               info = &per_cpu(doorbell_cpu_info, target);
+               set_bit(msg, &info->messages);
+               ppc_msgsnd(PPC_DBELL, 0, info->tag);
        }
-       else if(target == MSG_ALL_BUT_SELF) {
+       else if (target == MSG_ALL_BUT_SELF) {
                for_each_online_cpu(i) {
                        if (i == smp_processor_id())
                                continue;
-                       set_bit(msg, &dbell_smp_message[i]);
-                       ppc_msgsnd(PPC_DBELL, 0, i);
+                       info = &per_cpu(doorbell_cpu_info, i);
+                       set_bit(msg, &info->messages);
+                       ppc_msgsnd(PPC_DBELL, 0, info->tag);
                }
        }
        else { /* target == MSG_ALL */
-               for_each_online_cpu(i)
-                       set_bit(msg, &dbell_smp_message[i]);
+               for_each_online_cpu(i) {
+                       info = &per_cpu(doorbell_cpu_info, i);
+                       set_bit(msg, &info->messages);
+               }
                ppc_msgsnd(PPC_DBELL, PPC_DBELL_MSG_BRDCAST, 0);
        }
 }
 
 void doorbell_exception(struct pt_regs *regs)
 {
-       int cpu = smp_processor_id();
+       struct doorbell_cpu_info *info = &__get_cpu_var(doorbell_cpu_info);
        int msg;
 
-       if (num_online_cpus() < 2)
+       /* Warning: regs can be NULL when called from irq enable */
+
+       if (!info->messages || (num_online_cpus() < 2))
                return;
 
        for (msg = 0; msg < 4; msg++)
-               if (test_and_clear_bit(msg, &dbell_smp_message[cpu]))
+               if (test_and_clear_bit(msg, &info->messages))
                        smp_message_recv(msg);
 }
 
index a15f582300d82666e2bd8d07f9e19a7bf7399d18..4c3cde911c713fed528f19b8437b926977d2d9d7 100644 (file)
@@ -99,6 +99,8 @@ static void __init
 smp_85xx_setup_cpu(int cpu_nr)
 {
        mpic_setup_this_cpu();
+       if (cpu_has_feature(CPU_FTR_DBELL))
+               doorbell_setup_this_cpu();
 }
 
 struct smp_ops_t smp_85xx_ops = {
@@ -117,7 +119,7 @@ void __init mpc85xx_smp_init(void)
        }
 
        if (cpu_has_feature(CPU_FTR_DBELL))
-               smp_85xx_ops.message_pass = smp_dbell_message_pass;
+               smp_85xx_ops.message_pass = doorbell_message_pass;
 
        BUG_ON(!smp_85xx_ops.message_pass);