Skip to content

Commit

Permalink
powerpc/mm: Add SMP support to no-hash TLB handling
Browse files Browse the repository at this point in the history
This commit moves the whole no-hash TLB handling out of line into a
new tlb_nohash.c file, and implements some basic SMP support using
IPIs and/or broadcast tlbivax instructions.

Note that I'm using local invalidations for D->I cache coherency.

At worst, if another processor is trying to execute the same and
has the old entry in its TLB, it will just take a fault and re-do
the TLB flush locally (it won't re-do the cache flush in any case).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Dec 21, 2008
1 parent 7c03d65 commit f048aac
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 57 deletions.
4 changes: 2 additions & 2 deletions arch/powerpc/include/asm/highmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ static inline void *kmap_atomic_prot(struct page *page, enum km_type type, pgpro
BUG_ON(!pte_none(*(kmap_pte-idx)));
#endif
__set_pte_at(&init_mm, vaddr, kmap_pte-idx, mk_pte(page, prot));
local_flush_tlb_page(vaddr);
local_flush_tlb_page(NULL, vaddr);

return (void*) vaddr;
}
Expand Down Expand Up @@ -113,7 +113,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
* this pte without first remap it
*/
pte_clear(&init_mm, vaddr, kmap_pte-idx);
local_flush_tlb_page(vaddr);
local_flush_tlb_page(NULL, vaddr);
#endif
pagefault_enable();
}
Expand Down
16 changes: 16 additions & 0 deletions arch/powerpc/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@
*/
#define MMU_FTR_BIG_PHYS ASM_CONST(0x00020000)

/* Enable use of broadcast TLB invalidations. We don't always set it
* on processors that support it due to other constraints with the
* use of such invalidations
*/
#define MMU_FTR_USE_TLBIVAX_BCAST ASM_CONST(0x00040000)

/* Enable use of tlbilx invalidate-by-PID variant.
*/
#define MMU_FTR_USE_TLBILX_PID ASM_CONST(0x00080000)

/* This indicates that the processor cannot handle multiple outstanding
* broadcast tlbivax or tlbsync. This makes the code use a spinlock
* around such invalidate forms.
*/
#define MMU_FTR_LOCK_BCAST_INVAL ASM_CONST(0x00100000)

#ifndef __ASSEMBLY__
#include <asm/cputable.h>

Expand Down
84 changes: 38 additions & 46 deletions arch/powerpc/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
* - local_flush_tlb_page(vmaddr) flushes one page on the local processor
* - local_flush_tlb_mm(mm) flushes the specified mm context on
* the local processor
* - local_flush_tlb_page(vma, vmaddr) flushes one page on the local processor
* - flush_tlb_page_nohash(vma, vmaddr) flushes one page if SW loaded TLB
* - flush_tlb_range(vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
Expand All @@ -18,7 +20,7 @@
*/
#ifdef __KERNEL__

#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE)
#ifdef CONFIG_PPC_MMU_NOHASH
/*
* TLB flushing for software loaded TLB chips
*
Expand All @@ -31,59 +33,37 @@

#define MMU_NO_CONTEXT ((unsigned int)-1)

extern void _tlbie(unsigned long address, unsigned int pid);
extern void _tlbil_all(void);
extern void _tlbil_pid(unsigned int pid);
extern void _tlbil_va(unsigned long address, unsigned int pid);
extern void _tlbivax_bcast(unsigned long address, unsigned int pid);

#if defined(CONFIG_40x) || defined(CONFIG_8xx)
#define _tlbia() asm volatile ("tlbia; sync" : : : "memory")
#else /* CONFIG_44x || CONFIG_FSL_BOOKE */
extern void _tlbia(void);
#endif

static inline void local_flush_tlb_mm(struct mm_struct *mm)
{
_tlbil_pid(mm->context.id);
}

static inline void flush_tlb_mm(struct mm_struct *mm)
{
_tlbil_pid(mm->context.id);
}

static inline void local_flush_tlb_page(unsigned long vmaddr)
{
_tlbil_va(vmaddr, 0);
}

static inline void flush_tlb_page(struct vm_area_struct *vma,
unsigned long vmaddr)
{
_tlbil_va(vmaddr, vma ? vma->vm_mm->context.id : 0);
}
extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);

static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
unsigned long vmaddr)
{
flush_tlb_page(vma, vmaddr);
}
extern void local_flush_tlb_mm(struct mm_struct *mm);
extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);

static inline void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
_tlbil_pid(vma->vm_mm->context.id);
}
#ifdef CONFIG_SMP
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
#else
#define flush_tlb_mm(mm) local_flush_tlb_mm(mm)
#define flush_tlb_page(vma,addr) local_flush_tlb_page(vma,addr)
#endif
#define flush_tlb_page_nohash(vma,addr) flush_tlb_page(vma,addr)

static inline void flush_tlb_kernel_range(unsigned long start,
unsigned long end)
{
_tlbil_pid(0);
}
#elif defined(CONFIG_PPC_STD_MMU_32)

#elif defined(CONFIG_PPC32)
/*
* TLB flushing for "classic" hash-MMMU 32-bit CPUs, 6xx, 7xx, 7xxx
* TLB flushing for "classic" hash-MMU 32-bit CPUs, 6xx, 7xx, 7xxx
*/
extern void _tlbie(unsigned long address);
extern void _tlbia(void);
Expand All @@ -94,14 +74,20 @@ extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr
extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
static inline void local_flush_tlb_page(unsigned long vmaddr)
static inline void local_flush_tlb_page(struct vm_area_struct *vma,
unsigned long vmaddr)
{
flush_tlb_page(NULL, vmaddr);
flush_tlb_page(vma, vmaddr);
}
static inline void local_flush_tlb_mm(struct mm_struct *mm)
{
flush_tlb_mm(mm);
}

#else
#elif defined(CONFIG_PPC_STD_MMU_64)

/*
* TLB flushing for 64-bit has-MMU CPUs
* TLB flushing for 64-bit hash-MMU CPUs
*/

#include <linux/percpu.h>
Expand Down Expand Up @@ -151,11 +137,16 @@ extern void flush_hash_page(unsigned long va, real_pte_t pte, int psize,
extern void flush_hash_range(unsigned long number, int local);


static inline void local_flush_tlb_mm(struct mm_struct *mm)
{
}

static inline void flush_tlb_mm(struct mm_struct *mm)
{
}

static inline void local_flush_tlb_page(unsigned long vmaddr)
static inline void local_flush_tlb_page(struct vm_area_struct *vma,
unsigned long vmaddr)
{
}

Expand Down Expand Up @@ -183,7 +174,8 @@ static inline void flush_tlb_kernel_range(unsigned long start,
extern void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
unsigned long end);


#else
#error Unsupported MMU type
#endif

#endif /*__KERNEL__ */
Expand Down
9 changes: 9 additions & 0 deletions arch/powerpc/kernel/misc_32.S
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <asm/asm-offsets.h>
#include <asm/processor.h>
#include <asm/kexec.h>
#include <asm/bug.h>

.text

Expand Down Expand Up @@ -496,6 +497,14 @@ _GLOBAL(_tlbil_va)
blr
#endif /* CONFIG_FSL_BOOKE */

/*
* Nobody implements this yet
*/
_GLOBAL(_tlbivax_bcast)
1: trap
EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,0;
blr


/*
* Flush instruction cache.
Expand Down
6 changes: 0 additions & 6 deletions arch/powerpc/kernel/ppc_ksyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,6 @@ EXPORT_SYMBOL(giveup_spe);

#ifndef CONFIG_PPC64
EXPORT_SYMBOL(flush_instruction_cache);
EXPORT_SYMBOL(flush_tlb_kernel_range);
EXPORT_SYMBOL(flush_tlb_page);
EXPORT_SYMBOL(_tlbie);
#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE)
EXPORT_SYMBOL(_tlbil_va);
#endif
#endif
EXPORT_SYMBOL(__flush_icache_range);
EXPORT_SYMBOL(flush_dcache_range);
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ endif
obj-y := fault.o mem.o pgtable.o \
init_$(CONFIG_WORD_SIZE).o \
pgtable_$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_PPC_MMU_NOHASH) += mmu_context_nohash.o
obj-$(CONFIG_PPC_MMU_NOHASH) += mmu_context_nohash.o tlb_nohash.o
hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o
obj-$(CONFIG_PPC64) += hash_utils_64.o \
slb_low.o slb.o stab.o \
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
}
pte_update(ptep, 0, _PAGE_HWEXEC |
_PAGE_ACCESSED);
_tlbie(address, mm->context.id);
local_flush_tlb_page(vma, address);
pte_unmap_unlock(ptep, ptl);
up_read(&mm->mmap_sem);
return 0;
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/mm/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
* we invalidate the TLB here, thus avoiding dcbst
* misbehaviour.
*/
_tlbie(address, 0 /* 8xx doesn't care about PID */);
_tlbil_va(address, 0 /* 8xx doesn't care about PID */);
#endif
/* The _PAGE_USER test should really be _PAGE_EXEC, but
* older glibc versions execute some code from no-exec
Expand Down
4 changes: 4 additions & 0 deletions arch/powerpc/mm/tlb_hash32.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
flush_range(&init_mm, start, end);
FINISH_FLUSH;
}
EXPORT_SYMBOL(flush_tlb_kernel_range);

/*
* Flush all the (user) entries for the address space described by mm.
Expand All @@ -160,6 +161,7 @@ void flush_tlb_mm(struct mm_struct *mm)
flush_range(mp->vm_mm, mp->vm_start, mp->vm_end);
FINISH_FLUSH;
}
EXPORT_SYMBOL(flush_tlb_mm);

void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
Expand All @@ -176,6 +178,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
flush_hash_pages(mm->context.id, vmaddr, pmd_val(*pmd), 1);
FINISH_FLUSH;
}
EXPORT_SYMBOL(flush_tlb_page);

/*
* For each address in the range, find the pte for the address
Expand All @@ -188,3 +191,4 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
flush_range(vma->vm_mm, start, end);
FINISH_FLUSH;
}
EXPORT_SYMBOL(flush_tlb_range);
Loading

0 comments on commit f048aac

Please sign in to comment.