powerpc/book3s: Fix flush_tlb cpu_spec hook to take a generic argument.
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Fri, 19 Dec 2014 03:11:05 +0000 (08:41 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 16 Mar 2015 20:52:48 +0000 (07:52 +1100)
The flush_tlb hook in cpu_spec was introduced as a generic function hook
to invalidate TLBs. But the current implementation of flush_tlb hook
takes IS (invalidation selector) as an argument which is architecture
dependent. Hence, It is not right to have a generic routine where caller
has to pass non-generic argument.

This patch fixes this and makes flush_tlb hook as high level API.

Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/cputable.h
arch/powerpc/include/asm/mmu-hash64.h
arch/powerpc/kernel/cpu_setup_power.S
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/mce_power.c
arch/powerpc/kvm/book3s_hv_ras.c

index 5cf5a6d1068599480cb6f3effda0be5bdde45843..6367b8347dad9b0bceb3507573450b2e7c672400 100644 (file)
@@ -100,7 +100,7 @@ struct cpu_spec {
        /*
         * Processor specific routine to flush tlbs.
         */
-       void            (*flush_tlb)(unsigned long inval_selector);
+       void            (*flush_tlb)(unsigned int action);
 
 };
 
@@ -114,6 +114,12 @@ extern void do_feature_fixups(unsigned long value, void *fixup_start,
 
 extern const char *powerpc_base_platform;
 
+/* TLB flush actions. Used as argument to cpu_spec.flush_tlb() hook */
+enum {
+       TLB_INVAL_SCOPE_GLOBAL = 0,     /* invalidate all TLBs */
+       TLB_INVAL_SCOPE_LPID = 1,       /* invalidate TLBs for current LPID */
+};
+
 #endif /* __ASSEMBLY__ */
 
 /* CPU kernel features */
index 4f13c3ed7acf923fe4d9d0b4fac4568853b3ef35..1da6a81ce541fad8f083f23e3509dda137928d0a 100644 (file)
 #define TLBIEL_INVAL_SET_SHIFT 12
 
 #define POWER7_TLB_SETS                128     /* # sets in POWER7 TLB */
+#define POWER8_TLB_SETS                512     /* # sets in POWER8 TLB */
 
 #ifndef __ASSEMBLY__
 
index 46733535cc0b482a1ea35fe8411c556fb26fcb3b..9c9b7411b28bbc5c8c5c0d0b79752b605e80b116 100644 (file)
@@ -137,15 +137,11 @@ __init_HFSCR:
 /*
  * Clear the TLB using the specified IS form of tlbiel instruction
  * (invalidate by congruence class). P7 has 128 CCs., P8 has 512.
- *
- * r3 = IS field
  */
 __init_tlb_power7:
-       li      r3,0xc00        /* IS field = 0b11 */
-_GLOBAL(__flush_tlb_power7)
        li      r6,128
        mtctr   r6
-       mr      r7,r3           /* IS field */
+       li      r7,0xc00        /* IS field = 0b11 */
        ptesync
 2:     tlbiel  r7
        addi    r7,r7,0x1000
@@ -154,11 +150,9 @@ _GLOBAL(__flush_tlb_power7)
 1:     blr
 
 __init_tlb_power8:
-       li      r3,0xc00        /* IS field = 0b11 */
-_GLOBAL(__flush_tlb_power8)
        li      r6,512
        mtctr   r6
-       mr      r7,r3           /* IS field */
+       li      r7,0xc00        /* IS field = 0b11 */
        ptesync
 2:     tlbiel  r7
        addi    r7,r7,0x1000
index f337666768a76594b41b59f283efa3b891988193..7ed126bc9b183d2d1b6c15f6537c99dfd677ea9e 100644 (file)
@@ -71,8 +71,8 @@ extern void __restore_cpu_power7(void);
 extern void __setup_cpu_power8(unsigned long offset, struct cpu_spec* spec);
 extern void __restore_cpu_power8(void);
 extern void __restore_cpu_a2(void);
-extern void __flush_tlb_power7(unsigned long inval_selector);
-extern void __flush_tlb_power8(unsigned long inval_selector);
+extern void __flush_tlb_power7(unsigned int action);
+extern void __flush_tlb_power8(unsigned int action);
 extern long __machine_check_early_realmode_p7(struct pt_regs *regs);
 extern long __machine_check_early_realmode_p8(struct pt_regs *regs);
 #endif /* CONFIG_PPC64 */
index b6f123ab90edeacb7249bc5629e4ce952d135e4f..2c647b1e62e4b82d1157b39ef4086e29e066667a 100644 (file)
 #include <asm/mce.h>
 #include <asm/machdep.h>
 
+static void flush_tlb_206(unsigned int num_sets, unsigned int action)
+{
+       unsigned long rb;
+       unsigned int i;
+
+       switch (action) {
+       case TLB_INVAL_SCOPE_GLOBAL:
+               rb = TLBIEL_INVAL_SET;
+               break;
+       case TLB_INVAL_SCOPE_LPID:
+               rb = TLBIEL_INVAL_SET_LPID;
+               break;
+       default:
+               BUG();
+               break;
+       }
+
+       asm volatile("ptesync" : : : "memory");
+       for (i = 0; i < num_sets; i++) {
+               asm volatile("tlbiel %0" : : "r" (rb));
+               rb += 1 << TLBIEL_INVAL_SET_SHIFT;
+       }
+       asm volatile("ptesync" : : : "memory");
+}
+
+/*
+ * Generic routine to flush TLB on power7. This routine is used as
+ * flush_tlb hook in cpu_spec for Power7 processor.
+ *
+ * action => TLB_INVAL_SCOPE_GLOBAL:  Invalidate all TLBs.
+ *          TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID.
+ */
+void __flush_tlb_power7(unsigned int action)
+{
+       flush_tlb_206(POWER7_TLB_SETS, action);
+}
+
+/*
+ * Generic routine to flush TLB on power8. This routine is used as
+ * flush_tlb hook in cpu_spec for power8 processor.
+ *
+ * action => TLB_INVAL_SCOPE_GLOBAL:  Invalidate all TLBs.
+ *          TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID.
+ */
+void __flush_tlb_power8(unsigned int action)
+{
+       flush_tlb_206(POWER8_TLB_SETS, action);
+}
+
 /* flush SLBs and reload */
 static void flush_and_reload_slb(void)
 {
@@ -79,7 +128,7 @@ static long mce_handle_derror(uint64_t dsisr, uint64_t slb_error_bits)
        }
        if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) {
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL);
                /* reset error bits */
                dsisr &= ~P7_DSISR_MC_TLB_MULTIHIT_MFTLB;
        }
@@ -110,7 +159,7 @@ static long mce_handle_common_ierror(uint64_t srr1)
                break;
        case P7_SRR1_MC_IFETCH_TLB_MULTIHIT:
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb) {
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL);
                        handled = 1;
                }
                break;
index 60081bd75847276c8291ffffe156071eb6872194..93b5f5c9b4455ef6e769472ca35a2c8e91ba96be 100644 (file)
@@ -84,7 +84,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
                }
                if (dsisr & DSISR_MC_TLB_MULTI) {
                        if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                               cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
+                               cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID);
                        dsisr &= ~DSISR_MC_TLB_MULTI;
                }
                /* Any other errors we don't understand? */
@@ -102,7 +102,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
                break;
        case SRR1_MC_IFETCH_TLBMULTI:
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID);
                break;
        default:
                handled = 0;