Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4748276 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman
Browse files

powerpc/64s: Improve local TLB flush for boot and MCE on POWER9



There are several cases outside the normal address space management
where a CPU's entire local TLB is to be flushed:

  1. Booting the kernel, in case something has left stale entries in
     the TLB (e.g., kexec).

  2. Machine check, to clean corrupted TLB entries.

One other place where the TLB is flushed, is waking from deep idle
states. The flush is a side-effect of calling ->cpu_restore with the
intention of re-setting various SPRs. The flush itself is unnecessary
because in the first case, the TLB should not acquire new corrupted
TLB entries as part of sleep/wake (though they may be lost).

This type of TLB flush is coded inflexibly, several times for each CPU
type, and they have a number of problems with ISA v3.0B:

- The current radix mode of the MMU is not taken into account, it is
  always done as a hash flushn For IS=2 (LPID-matching flush from host)
  and IS=3 with HV=0 (guest kernel flush), tlbie(l) is undefined if
  the R field does not match the current radix mode.

- ISA v3.0B hash must flush the partition and process table caches as
  well.

- ISA v3.0B radix must flush partition and process scoped translations,
  partition and process table caches, and also the page walk cache.

So consolidate the flushing code and implement it in C and inline asm
under the mm/ directory with the rest of the flush code. Add ISA v3.0B
cases for radix and hash, and use the radix flush in radix environment.

Provide a way for IS=2 (LPID flush) to specify the radix mode of the
partition. Have KVM pass in the radix mode of the guest.

Take out the flushes from early cputable/dt_cpu_ftrs detection hooks,
and move it later in the boot process after, the MMU registers are set
up and before relocation is first turned on.

The TLB flush is no longer called when restoring from deep idle states.
This was not be done as a separate step because booting secondaries
uses the same cpu_restore as idle restore, which needs the TLB flush.

Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 4552d128
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@ static inline void arch_leave_lazy_mmu_mode(void)


#define arch_flush_lazy_mmu_mode()      do {} while (0)
#define arch_flush_lazy_mmu_mode()      do {} while (0)


extern void hash__tlbiel_all(unsigned int action);


extern void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize,
extern void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize,
			    int ssize, unsigned long flags);
			    int ssize, unsigned long flags);
+3 −0
Original line number Original line Diff line number Diff line
@@ -11,6 +11,8 @@ static inline int mmu_get_ap(int psize)
	return mmu_psize_defs[psize].ap;
	return mmu_psize_defs[psize].ap;
}
}


extern void radix__tlbiel_all(unsigned int action);

extern void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma,
extern void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma,
					   unsigned long start, unsigned long end);
					   unsigned long start, unsigned long end);
extern void radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
extern void radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
@@ -47,4 +49,5 @@ extern void radix__flush_tlb_lpid(unsigned long lpid);
extern void radix__flush_tlb_all(void);
extern void radix__flush_tlb_all(void);
extern void radix__flush_tlb_pte_p9_dd1(unsigned long old_pte, struct mm_struct *mm,
extern void radix__flush_tlb_pte_p9_dd1(unsigned long old_pte, struct mm_struct *mm,
					unsigned long address);
					unsigned long address);

#endif
#endif
+34 −0
Original line number Original line Diff line number Diff line
@@ -8,6 +8,40 @@
#include <asm/book3s/64/tlbflush-hash.h>
#include <asm/book3s/64/tlbflush-hash.h>
#include <asm/book3s/64/tlbflush-radix.h>
#include <asm/book3s/64/tlbflush-radix.h>


/* TLB flush actions. Used as argument to tlbiel_all() */
enum {
	TLB_INVAL_SCOPE_GLOBAL = 0,	/* invalidate all TLBs */
	TLB_INVAL_SCOPE_LPID = 1,	/* invalidate TLBs for current LPID */
};

static inline void tlbiel_all(void)
{
	/*
	 * This is used for host machine check and bootup.
	 *
	 * This uses early_radix_enabled and implementations use
	 * early_cpu_has_feature etc because that works early in boot
	 * and this is the machine check path which is not performance
	 * critical.
	 */
	if (early_radix_enabled())
		radix__tlbiel_all(TLB_INVAL_SCOPE_GLOBAL);
	else
		hash__tlbiel_all(TLB_INVAL_SCOPE_GLOBAL);
}

static inline void tlbiel_all_lpid(bool radix)
{
	/*
	 * This is used for guest machine check.
	 */
	if (radix)
		radix__tlbiel_all(TLB_INVAL_SCOPE_LPID);
	else
		hash__tlbiel_all(TLB_INVAL_SCOPE_LPID);
}


#define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
#define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE
static inline void flush_pmd_tlb_range(struct vm_area_struct *vma,
static inline void flush_pmd_tlb_range(struct vm_area_struct *vma,
				       unsigned long start, unsigned long end)
				       unsigned long start, unsigned long end)
+0 −12
Original line number Original line Diff line number Diff line
@@ -107,12 +107,6 @@ struct cpu_spec {
	 * called in real mode to handle SLB and TLB errors.
	 * called in real mode to handle SLB and TLB errors.
	 */
	 */
	long		(*machine_check_early)(struct pt_regs *regs);
	long		(*machine_check_early)(struct pt_regs *regs);

	/*
	 * Processor specific routine to flush tlbs.
	 */
	void		(*flush_tlb)(unsigned int action);

};
};


extern struct cpu_spec		*cur_cpu_spec;
extern struct cpu_spec		*cur_cpu_spec;
@@ -133,12 +127,6 @@ extern void cpu_feature_keys_init(void);
static inline void cpu_feature_keys_init(void) { }
static inline void cpu_feature_keys_init(void) { }
#endif
#endif


/* 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__ */
#endif /* __ASSEMBLY__ */


/* CPU kernel features */
/* CPU kernel features */
+0 −50
Original line number Original line Diff line number Diff line
@@ -31,7 +31,6 @@ _GLOBAL(__setup_cpu_power7)
	mfspr	r3,SPRN_LPCR
	mfspr	r3,SPRN_LPCR
	li	r4,(LPCR_LPES1 >> LPCR_LPES_SH)
	li	r4,(LPCR_LPES1 >> LPCR_LPES_SH)
	bl	__init_LPCR_ISA206
	bl	__init_LPCR_ISA206
	bl	__init_tlb_power7
	mtlr	r11
	mtlr	r11
	blr
	blr


@@ -45,7 +44,6 @@ _GLOBAL(__restore_cpu_power7)
	mfspr	r3,SPRN_LPCR
	mfspr	r3,SPRN_LPCR
	li	r4,(LPCR_LPES1 >> LPCR_LPES_SH)
	li	r4,(LPCR_LPES1 >> LPCR_LPES_SH)
	bl	__init_LPCR_ISA206
	bl	__init_LPCR_ISA206
	bl	__init_tlb_power7
	mtlr	r11
	mtlr	r11
	blr
	blr


@@ -64,7 +62,6 @@ _GLOBAL(__setup_cpu_power8)
	li	r4,0 /* LPES = 0 */
	li	r4,0 /* LPES = 0 */
	bl	__init_LPCR_ISA206
	bl	__init_LPCR_ISA206
	bl	__init_HFSCR
	bl	__init_HFSCR
	bl	__init_tlb_power8
	bl	__init_PMU_HV
	bl	__init_PMU_HV
	bl	__init_PMU_HV_ISA207
	bl	__init_PMU_HV_ISA207
	mtlr	r11
	mtlr	r11
@@ -86,7 +83,6 @@ _GLOBAL(__restore_cpu_power8)
	li	r4,0 /* LPES = 0 */
	li	r4,0 /* LPES = 0 */
	bl	__init_LPCR_ISA206
	bl	__init_LPCR_ISA206
	bl	__init_HFSCR
	bl	__init_HFSCR
	bl	__init_tlb_power8
	bl	__init_PMU_HV
	bl	__init_PMU_HV
	bl	__init_PMU_HV_ISA207
	bl	__init_PMU_HV_ISA207
	mtlr	r11
	mtlr	r11
@@ -110,7 +106,6 @@ _GLOBAL(__setup_cpu_power9)
	li	r4,0 /* LPES = 0 */
	li	r4,0 /* LPES = 0 */
	bl	__init_LPCR_ISA300
	bl	__init_LPCR_ISA300
	bl	__init_HFSCR
	bl	__init_HFSCR
	bl	__init_tlb_power9
	bl	__init_PMU_HV
	bl	__init_PMU_HV
	mtlr	r11
	mtlr	r11
	blr
	blr
@@ -134,7 +129,6 @@ _GLOBAL(__restore_cpu_power9)
	li	r4,0 /* LPES = 0 */
	li	r4,0 /* LPES = 0 */
	bl	__init_LPCR_ISA300
	bl	__init_LPCR_ISA300
	bl	__init_HFSCR
	bl	__init_HFSCR
	bl	__init_tlb_power9
	bl	__init_PMU_HV
	bl	__init_PMU_HV
	mtlr	r11
	mtlr	r11
	blr
	blr
@@ -192,50 +186,6 @@ __init_HFSCR:
	mtspr	SPRN_HFSCR,r3
	mtspr	SPRN_HFSCR,r3
	blr
	blr


/*
 * Clear the TLB using the specified IS form of tlbiel instruction
 * (invalidate by congruence class). P7 has 128 CCs., P8 has 512.
 */
__init_tlb_power7:
	li	r6,POWER7_TLB_SETS
	mtctr	r6
	li	r7,0xc00	/* IS field = 0b11 */
	ptesync
2:	tlbiel	r7
	addi	r7,r7,0x1000
	bdnz	2b
	ptesync
1:	blr

__init_tlb_power8:
	li	r6,POWER8_TLB_SETS
	mtctr	r6
	li	r7,0xc00	/* IS field = 0b11 */
	ptesync
2:	tlbiel	r7
	addi	r7,r7,0x1000
	bdnz	2b
	ptesync
1:	blr

/*
 * Flush the TLB in hash mode. Hash must flush with RIC=2 once for process
 * and one for partition scope to clear process and partition table entries.
 */
__init_tlb_power9:
	li	r6,POWER9_TLB_SETS_HASH - 1
	mtctr	r6
	li	r7,0xc00	/* IS field = 0b11 */
	li	r8,0
	ptesync
	PPC_TLBIEL(7, 8, 2, 1, 0)
	PPC_TLBIEL(7, 8, 2, 0, 0)
2:	addi	r7,r7,0x1000
	PPC_TLBIEL(7, 8, 0, 0, 0)
	bdnz	2b
	ptesync
1:	blr

__init_PMU_HV:
__init_PMU_HV:
	li	r5,0
	li	r5,0
	mtspr	SPRN_MMCRC,r5
	mtspr	SPRN_MMCRC,r5
Loading