diff options
Diffstat (limited to 'arch/powerpc/mm')
| -rw-r--r-- | arch/powerpc/mm/fsl_booke_mmu.c | 26 | ||||
| -rw-r--r-- | arch/powerpc/mm/hash_native_64.c | 23 | ||||
| -rw-r--r-- | arch/powerpc/mm/hash_utils_64.c | 7 | ||||
| -rw-r--r-- | arch/powerpc/mm/hugetlbpage.c | 40 | ||||
| -rw-r--r-- | arch/powerpc/mm/mmu_decl.h | 4 | ||||
| -rw-r--r-- | arch/powerpc/mm/numa.c | 7 | ||||
| -rw-r--r-- | arch/powerpc/mm/slb.c | 49 | ||||
| -rw-r--r-- | arch/powerpc/mm/tlb_hash64.c | 9 | ||||
| -rw-r--r-- | arch/powerpc/mm/tlb_low_64e.S | 25 | ||||
| -rw-r--r-- | arch/powerpc/mm/tlb_nohash.c | 41 | ||||
| -rw-r--r-- | arch/powerpc/mm/tlb_nohash_low.S | 63 | 
11 files changed, 218 insertions, 76 deletions
| diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index 354ba3c09ef3..f3afe3d97f6b 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -141,8 +141,6 @@ static void settlbcam(int index, unsigned long virt, phys_addr_t phys,  	tlbcam_addrs[index].start = virt;  	tlbcam_addrs[index].limit = virt + size - 1;  	tlbcam_addrs[index].phys = phys; - -	loadcam_entry(index);  }  unsigned long calc_cam_sz(unsigned long ram, unsigned long virt, @@ -171,7 +169,8 @@ unsigned long calc_cam_sz(unsigned long ram, unsigned long virt,  }  static unsigned long map_mem_in_cams_addr(phys_addr_t phys, unsigned long virt, -					unsigned long ram, int max_cam_idx) +					unsigned long ram, int max_cam_idx, +					bool dryrun)  {  	int i;  	unsigned long amount_mapped = 0; @@ -181,13 +180,20 @@ static unsigned long map_mem_in_cams_addr(phys_addr_t phys, unsigned long virt,  		unsigned long cam_sz;  		cam_sz = calc_cam_sz(ram, virt, phys); -		settlbcam(i, virt, phys, cam_sz, pgprot_val(PAGE_KERNEL_X), 0); +		if (!dryrun) +			settlbcam(i, virt, phys, cam_sz, +				  pgprot_val(PAGE_KERNEL_X), 0);  		ram -= cam_sz;  		amount_mapped += cam_sz;  		virt += cam_sz;  		phys += cam_sz;  	} + +	if (dryrun) +		return amount_mapped; + +	loadcam_multi(0, i, max_cam_idx);  	tlbcam_index = i;  #ifdef CONFIG_PPC64 @@ -199,12 +205,12 @@ static unsigned long map_mem_in_cams_addr(phys_addr_t phys, unsigned long virt,  	return amount_mapped;  } -unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx) +unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx, bool dryrun)  {  	unsigned long virt = PAGE_OFFSET;  	phys_addr_t phys = memstart_addr; -	return map_mem_in_cams_addr(phys, virt, ram, max_cam_idx); +	return map_mem_in_cams_addr(phys, virt, ram, max_cam_idx, dryrun);  }  #ifdef CONFIG_PPC32 @@ -235,7 +241,7 @@ void __init adjust_total_lowmem(void)  	ram = min((phys_addr_t)__max_low_memory, (phys_addr_t)total_lowmem);  	i = switch_to_as1(); -	__max_low_memory = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM); +	__max_low_memory = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, false);  	restore_to_as0(i, 0, 0, 1);  	pr_info("Memory CAM mapping: "); @@ -303,10 +309,12 @@ notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)  		n = switch_to_as1();  		/* map a 64M area for the second relocation */  		if (memstart_addr > start) -			map_mem_in_cams(0x4000000, CONFIG_LOWMEM_CAM_NUM); +			map_mem_in_cams(0x4000000, CONFIG_LOWMEM_CAM_NUM, +					false);  		else  			map_mem_in_cams_addr(start, PAGE_OFFSET + offset, -					0x4000000, CONFIG_LOWMEM_CAM_NUM); +					0x4000000, CONFIG_LOWMEM_CAM_NUM, +					false);  		restore_to_as0(n, offset, __va(dt_ptr), 1);  		/* We should never reach here */  		panic("Relocation error"); diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 13befa35d8a8..c8822af10a58 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -582,13 +582,21 @@ static void hpte_decode(struct hash_pte *hpte, unsigned long slot,   * be when they isi), and we are the only one left.  We rely on our kernel   * mapping being 0xC0's and the hardware ignoring those two real bits.   * + * This must be called with interrupts disabled. + * + * Taking the native_tlbie_lock is unsafe here due to the possibility of + * lockdep being on. On pre POWER5 hardware, not taking the lock could + * cause deadlock. POWER5 and newer not taking the lock is fine. This only + * gets called during boot before secondary CPUs have come up and during + * crashdump and all bets are off anyway. + *   * TODO: add batching support when enabled.  remember, no dynamic memory here,   * athough there is the control page available...   */  static void native_hpte_clear(void)  {  	unsigned long vpn = 0; -	unsigned long slot, slots, flags; +	unsigned long slot, slots;  	struct hash_pte *hptep = htab_address;  	unsigned long hpte_v;  	unsigned long pteg_count; @@ -596,13 +604,6 @@ static void native_hpte_clear(void)  	pteg_count = htab_hash_mask + 1; -	local_irq_save(flags); - -	/* we take the tlbie lock and hold it.  Some hardware will -	 * deadlock if we try to tlbie from two processors at once. -	 */ -	raw_spin_lock(&native_tlbie_lock); -  	slots = pteg_count * HPTES_PER_GROUP;  	for (slot = 0; slot < slots; slot++, hptep++) { @@ -614,8 +615,8 @@ static void native_hpte_clear(void)  		hpte_v = be64_to_cpu(hptep->v);  		/* -		 * Call __tlbie() here rather than tlbie() since we -		 * already hold the native_tlbie_lock. +		 * Call __tlbie() here rather than tlbie() since we can't take the +		 * native_tlbie_lock.  		 */  		if (hpte_v & HPTE_V_VALID) {  			hpte_decode(hptep, slot, &psize, &apsize, &ssize, &vpn); @@ -625,8 +626,6 @@ static void native_hpte_clear(void)  	}  	asm volatile("eieio; tlbsync; ptesync":::"memory"); -	raw_spin_unlock(&native_tlbie_lock); -	local_irq_restore(flags);  }  /* diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index aee70171355b..7f9616f7c479 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -994,6 +994,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,  		 unsigned long access, unsigned long trap,  		 unsigned long flags)  { +	bool is_thp;  	enum ctx_state prev_state = exception_enter();  	pgd_t *pgdir;  	unsigned long vsid; @@ -1068,7 +1069,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,  #endif /* CONFIG_PPC_64K_PAGES */  	/* Get PTE and page size from page tables */ -	ptep = __find_linux_pte_or_hugepte(pgdir, ea, &hugeshift); +	ptep = __find_linux_pte_or_hugepte(pgdir, ea, &is_thp, &hugeshift);  	if (ptep == NULL || !pte_present(*ptep)) {  		DBG_LOW(" no PTE !\n");  		rc = 1; @@ -1088,7 +1089,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,  	}  	if (hugeshift) { -		if (pmd_trans_huge(*(pmd_t *)ptep)) +		if (is_thp)  			rc = __hash_page_thp(ea, access, vsid, (pmd_t *)ptep,  					     trap, flags, ssize, psize);  #ifdef CONFIG_HUGETLB_PAGE @@ -1243,7 +1244,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,  	 * THP pages use update_mmu_cache_pmd. We don't do  	 * hash preload there. Hence can ignore THP here  	 */ -	ptep = find_linux_pte_or_hugepte(pgdir, ea, &hugepage_shift); +	ptep = find_linux_pte_or_hugepte(pgdir, ea, NULL, &hugepage_shift);  	if (!ptep)  		goto out_exit; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 06c14523b787..9833fee493ec 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -89,6 +89,25 @@ int pgd_huge(pgd_t pgd)  	 */  	return ((pgd_val(pgd) & 0x3) != 0x0);  } + +#if defined(CONFIG_PPC_64K_PAGES) && defined(CONFIG_DEBUG_VM) +/* + * This enables us to catch the wrong page directory format + * Moved here so that we can use WARN() in the call. + */ +int hugepd_ok(hugepd_t hpd) +{ +	bool is_hugepd; + +	/* +	 * We should not find this format in page directory, warn otherwise. +	 */ +	is_hugepd = (((hpd.pd & 0x3) == 0x0) && ((hpd.pd & HUGEPD_SHIFT_MASK) != 0)); +	WARN(is_hugepd, "Found wrong page directory format\n"); +	return 0; +} +#endif +  #else  int pmd_huge(pmd_t pmd)  { @@ -109,7 +128,7 @@ int pgd_huge(pgd_t pgd)  pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)  {  	/* Only called for hugetlbfs pages, hence can ignore THP */ -	return __find_linux_pte_or_hugepte(mm->pgd, addr, NULL); +	return __find_linux_pte_or_hugepte(mm->pgd, addr, NULL, NULL);  }  static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, @@ -684,13 +703,14 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb,  struct page *  follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)  { +	bool is_thp;  	pte_t *ptep, pte;  	unsigned shift;  	unsigned long mask, flags;  	struct page *page = ERR_PTR(-EINVAL);  	local_irq_save(flags); -	ptep = find_linux_pte_or_hugepte(mm->pgd, address, &shift); +	ptep = find_linux_pte_or_hugepte(mm->pgd, address, &is_thp, &shift);  	if (!ptep)  		goto no_page;  	pte = READ_ONCE(*ptep); @@ -699,7 +719,7 @@ follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)  	 * Transparent hugepages are handled by generic code. We can skip them  	 * here.  	 */ -	if (!shift || pmd_trans_huge(__pmd(pte_val(pte)))) +	if (!shift || is_thp)  		goto no_page;  	if (!pte_present(pte)) { @@ -956,7 +976,7 @@ void flush_dcache_icache_hugepage(struct page *page)   */  pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, -				   unsigned *shift) +				   bool *is_thp, unsigned *shift)  {  	pgd_t pgd, *pgdp;  	pud_t pud, *pudp; @@ -968,6 +988,9 @@ pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea,  	if (shift)  		*shift = 0; +	if (is_thp) +		*is_thp = false; +  	pgdp = pgdir + pgd_index(ea);  	pgd  = READ_ONCE(*pgdp);  	/* @@ -1015,7 +1038,14 @@ pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea,  			if (pmd_none(pmd))  				return NULL; -			if (pmd_huge(pmd) || pmd_large(pmd)) { +			if (pmd_trans_huge(pmd)) { +				if (is_thp) +					*is_thp = true; +				ret_pte = (pte_t *) pmdp; +				goto out; +			} + +			if (pmd_huge(pmd)) {  				ret_pte = (pte_t *) pmdp;  				goto out;  			} else if (is_hugepd(__hugepd(pmd_val(pmd)))) diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index 085b66b10891..9f58ff44a075 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -141,7 +141,8 @@ extern void MMU_init_hw(void);  extern unsigned long mmu_mapin_ram(unsigned long top);  #elif defined(CONFIG_PPC_FSL_BOOK3E) -extern unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx); +extern unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx, +				     bool dryrun);  extern unsigned long calc_cam_sz(unsigned long ram, unsigned long virt,  				 phys_addr_t phys);  #ifdef CONFIG_PPC32 @@ -152,6 +153,7 @@ extern int switch_to_as1(void);  extern void restore_to_as0(int esel, int offset, void *dt_ptr, int bootcpu);  #endif  extern void loadcam_entry(unsigned int index); +extern void loadcam_multi(int first_idx, int num, int tmp_idx);  struct tlbcam {  	u32	MAS0; diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 8b9502adaf79..669a15e7fa76 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -80,7 +80,7 @@ static void __init setup_node_to_cpumask_map(void)  		setup_nr_node_ids();  	/* allocate the map */ -	for (node = 0; node < nr_node_ids; node++) +	for_each_node(node)  		alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);  	/* cpumask_of_node() will now work */ @@ -276,7 +276,6 @@ static int of_node_to_nid_single(struct device_node *device)  /* Walk the device tree upwards, looking for an associativity id */  int of_node_to_nid(struct device_node *device)  { -	struct device_node *tmp;  	int nid = -1;  	of_node_get(device); @@ -285,9 +284,7 @@ int of_node_to_nid(struct device_node *device)  		if (nid != -1)  			break; -	        tmp = device; -		device = of_get_parent(tmp); -		of_node_put(tmp); +		device = of_get_next_parent(device);  	}  	of_node_put(device); diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index 8a32a2be3c53..515730e499fe 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c @@ -25,6 +25,11 @@  #include <asm/udbg.h>  #include <asm/code-patching.h> +enum slb_index { +	LINEAR_INDEX	= 0, /* Kernel linear map  (0xc000000000000000) */ +	VMALLOC_INDEX	= 1, /* Kernel virtual map (0xd000000000000000) */ +	KSTACK_INDEX	= 2, /* Kernel stack map */ +};  extern void slb_allocate_realmode(unsigned long ea);  extern void slb_allocate_user(unsigned long ea); @@ -41,9 +46,9 @@ static void slb_allocate(unsigned long ea)  	(((ssize) == MMU_SEGSIZE_256M)? ESID_MASK: ESID_MASK_1T)  static inline unsigned long mk_esid_data(unsigned long ea, int ssize, -					 unsigned long entry) +					 enum slb_index index)  { -	return (ea & slb_esid_mask(ssize)) | SLB_ESID_V | entry; +	return (ea & slb_esid_mask(ssize)) | SLB_ESID_V | index;  }  static inline unsigned long mk_vsid_data(unsigned long ea, int ssize, @@ -55,39 +60,39 @@ static inline unsigned long mk_vsid_data(unsigned long ea, int ssize,  static inline void slb_shadow_update(unsigned long ea, int ssize,  				     unsigned long flags, -				     unsigned long entry) +				     enum slb_index index)  { +	struct slb_shadow *p = get_slb_shadow(); +  	/*  	 * Clear the ESID first so the entry is not valid while we are  	 * updating it.  No write barriers are needed here, provided  	 * we only update the current CPU's SLB shadow buffer.  	 */ -	get_slb_shadow()->save_area[entry].esid = 0; -	get_slb_shadow()->save_area[entry].vsid = -				cpu_to_be64(mk_vsid_data(ea, ssize, flags)); -	get_slb_shadow()->save_area[entry].esid = -				cpu_to_be64(mk_esid_data(ea, ssize, entry)); +	p->save_area[index].esid = 0; +	p->save_area[index].vsid = cpu_to_be64(mk_vsid_data(ea, ssize, flags)); +	p->save_area[index].esid = cpu_to_be64(mk_esid_data(ea, ssize, index));  } -static inline void slb_shadow_clear(unsigned long entry) +static inline void slb_shadow_clear(enum slb_index index)  { -	get_slb_shadow()->save_area[entry].esid = 0; +	get_slb_shadow()->save_area[index].esid = 0;  }  static inline void create_shadowed_slbe(unsigned long ea, int ssize,  					unsigned long flags, -					unsigned long entry) +					enum slb_index index)  {  	/*  	 * Updating the shadow buffer before writing the SLB ensures  	 * we don't get a stale entry here if we get preempted by PHYP  	 * between these two statements.  	 */ -	slb_shadow_update(ea, ssize, flags, entry); +	slb_shadow_update(ea, ssize, flags, index);  	asm volatile("slbmte  %0,%1" :  		     : "r" (mk_vsid_data(ea, ssize, flags)), -		       "r" (mk_esid_data(ea, ssize, entry)) +		       "r" (mk_esid_data(ea, ssize, index))  		     : "memory" );  } @@ -103,16 +108,16 @@ static void __slb_flush_and_rebolt(void)  	lflags = SLB_VSID_KERNEL | linear_llp;  	vflags = SLB_VSID_KERNEL | vmalloc_llp; -	ksp_esid_data = mk_esid_data(get_paca()->kstack, mmu_kernel_ssize, 2); +	ksp_esid_data = mk_esid_data(get_paca()->kstack, mmu_kernel_ssize, KSTACK_INDEX);  	if ((ksp_esid_data & ~0xfffffffUL) <= PAGE_OFFSET) {  		ksp_esid_data &= ~SLB_ESID_V;  		ksp_vsid_data = 0; -		slb_shadow_clear(2); +		slb_shadow_clear(KSTACK_INDEX);  	} else {  		/* Update stack entry; others don't change */ -		slb_shadow_update(get_paca()->kstack, mmu_kernel_ssize, lflags, 2); +		slb_shadow_update(get_paca()->kstack, mmu_kernel_ssize, lflags, KSTACK_INDEX);  		ksp_vsid_data = -			be64_to_cpu(get_slb_shadow()->save_area[2].vsid); +			be64_to_cpu(get_slb_shadow()->save_area[KSTACK_INDEX].vsid);  	}  	/* We need to do this all in asm, so we're sure we don't touch @@ -151,7 +156,7 @@ void slb_vmalloc_update(void)  	unsigned long vflags;  	vflags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_vmalloc_psize].sllp; -	slb_shadow_update(VMALLOC_START, mmu_kernel_ssize, vflags, 1); +	slb_shadow_update(VMALLOC_START, mmu_kernel_ssize, vflags, VMALLOC_INDEX);  	slb_flush_and_rebolt();  } @@ -326,19 +331,19 @@ void slb_initialize(void)  	asm volatile("isync":::"memory");  	asm volatile("slbmte  %0,%0"::"r" (0) : "memory");  	asm volatile("isync; slbia; isync":::"memory"); -	create_shadowed_slbe(PAGE_OFFSET, mmu_kernel_ssize, lflags, 0); -	create_shadowed_slbe(VMALLOC_START, mmu_kernel_ssize, vflags, 1); +	create_shadowed_slbe(PAGE_OFFSET, mmu_kernel_ssize, lflags, LINEAR_INDEX); +	create_shadowed_slbe(VMALLOC_START, mmu_kernel_ssize, vflags, VMALLOC_INDEX);  	/* For the boot cpu, we're running on the stack in init_thread_union,  	 * which is in the first segment of the linear mapping, and also  	 * get_paca()->kstack hasn't been initialized yet.  	 * For secondary cpus, we need to bolt the kernel stack entry now.  	 */ -	slb_shadow_clear(2); +	slb_shadow_clear(KSTACK_INDEX);  	if (raw_smp_processor_id() != boot_cpuid &&  	    (get_paca()->kstack & slb_esid_mask(mmu_kernel_ssize)) > PAGE_OFFSET)  		create_shadowed_slbe(get_paca()->kstack, -				     mmu_kernel_ssize, lflags, 2); +				     mmu_kernel_ssize, lflags, KSTACK_INDEX);  	asm volatile("isync":::"memory");  } diff --git a/arch/powerpc/mm/tlb_hash64.c b/arch/powerpc/mm/tlb_hash64.c index c522969f012d..f7b80391bee7 100644 --- a/arch/powerpc/mm/tlb_hash64.c +++ b/arch/powerpc/mm/tlb_hash64.c @@ -190,6 +190,7 @@ void tlb_flush(struct mmu_gather *tlb)  void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,  			      unsigned long end)  { +	bool is_thp;  	int hugepage_shift;  	unsigned long flags; @@ -208,21 +209,21 @@ void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,  	local_irq_save(flags);  	arch_enter_lazy_mmu_mode();  	for (; start < end; start += PAGE_SIZE) { -		pte_t *ptep = find_linux_pte_or_hugepte(mm->pgd, start, +		pte_t *ptep = find_linux_pte_or_hugepte(mm->pgd, start, &is_thp,  							&hugepage_shift);  		unsigned long pte;  		if (ptep == NULL)  			continue;  		pte = pte_val(*ptep); -		if (hugepage_shift) +		if (is_thp)  			trace_hugepage_invalidate(start, pte);  		if (!(pte & _PAGE_HASHPTE))  			continue; -		if (unlikely(hugepage_shift && pmd_trans_huge(*(pmd_t *)pte))) +		if (unlikely(is_thp))  			hpte_do_hugepage_flush(mm, start, (pmd_t *)ptep, pte);  		else -			hpte_need_flush(mm, start, ptep, pte, 0); +			hpte_need_flush(mm, start, ptep, pte, hugepage_shift);  	}  	arch_leave_lazy_mmu_mode();  	local_irq_restore(flags); diff --git a/arch/powerpc/mm/tlb_low_64e.S b/arch/powerpc/mm/tlb_low_64e.S index e4185581c5a7..29d6987c37ba 100644 --- a/arch/powerpc/mm/tlb_low_64e.S +++ b/arch/powerpc/mm/tlb_low_64e.S @@ -68,11 +68,17 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)  	ld	r14,PACAPGD(r13)  	std	r15,EX_TLB_R15(r12)  	std	r10,EX_TLB_CR(r12) +#ifdef CONFIG_PPC_FSL_BOOK3E +	std	r7,EX_TLB_R7(r12) +#endif  	TLB_MISS_PROLOG_STATS  .endm  .macro tlb_epilog_bolted  	ld	r14,EX_TLB_CR(r12) +#ifdef CONFIG_PPC_FSL_BOOK3E +	ld	r7,EX_TLB_R7(r12) +#endif  	ld	r10,EX_TLB_R10(r12)  	ld	r11,EX_TLB_R11(r12)  	ld	r13,EX_TLB_R13(r12) @@ -297,6 +303,7 @@ itlb_miss_fault_bolted:   * r13 = PACA   * r11 = tlb_per_core ptr   * r10 = crap (free to use) + * r7  = esel_next   */  tlb_miss_common_e6500:  	crmove	cr2*4+2,cr0*4+2		/* cr2.eq != 0 if kernel address */ @@ -325,7 +332,11 @@ BEGIN_FTR_SECTION		/* CPU_FTR_SMT */  	bne	10b  	b	1b  	.previous +END_FTR_SECTION_IFSET(CPU_FTR_SMT) + +	lbz	r7,TCD_ESEL_NEXT(r11) +BEGIN_FTR_SECTION		/* CPU_FTR_SMT */  	/*  	 * Erratum A-008139 says that we can't use tlbwe to change  	 * an indirect entry in any way (including replacing or @@ -334,8 +345,7 @@ BEGIN_FTR_SECTION		/* CPU_FTR_SMT */  	 * with tlbilx before overwriting.  	 */ -	lbz	r15,TCD_ESEL_NEXT(r11) -	rlwinm	r10,r15,16,0xff0000 +	rlwinm	r10,r7,16,0xff0000  	oris	r10,r10,MAS0_TLBSEL(1)@h  	mtspr	SPRN_MAS0,r10  	isync @@ -429,15 +439,14 @@ ALT_FTR_SECTION_END_IFSET(CPU_FTR_SMT)  	mtspr	SPRN_MAS2,r15  tlb_miss_huge_done_e6500: -	lbz	r15,TCD_ESEL_NEXT(r11)  	lbz	r16,TCD_ESEL_MAX(r11)  	lbz	r14,TCD_ESEL_FIRST(r11) -	rlwimi	r10,r15,16,0x00ff0000	/* insert esel_next into MAS0 */ -	addi	r15,r15,1		/* increment esel_next */ +	rlwimi	r10,r7,16,0x00ff0000	/* insert esel_next into MAS0 */ +	addi	r7,r7,1			/* increment esel_next */  	mtspr	SPRN_MAS0,r10 -	cmpw	r15,r16 -	iseleq	r15,r14,r15		/* if next == last use first */ -	stb	r15,TCD_ESEL_NEXT(r11) +	cmpw	r7,r16 +	iseleq	r7,r14,r7		/* if next == last use first */ +	stb	r7,TCD_ESEL_NEXT(r11)  	tlbwe diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index 723a099f6be3..bb04e4df3100 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -42,6 +42,7 @@  #include <asm/tlbflush.h>  #include <asm/tlb.h>  #include <asm/code-patching.h> +#include <asm/cputhreads.h>  #include <asm/hugetlb.h>  #include <asm/paca.h> @@ -628,10 +629,26 @@ static void early_init_this_mmu(void)  #ifdef CONFIG_PPC_FSL_BOOK3E  	if (mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {  		unsigned int num_cams; +		int __maybe_unused cpu = smp_processor_id(); +		bool map = true;  		/* use a quarter of the TLBCAM for bolted linear map */  		num_cams = (mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY) / 4; -		linear_map_top = map_mem_in_cams(linear_map_top, num_cams); + +		/* +		 * Only do the mapping once per core, or else the +		 * transient mapping would cause problems. +		 */ +#ifdef CONFIG_SMP +		if (cpu != boot_cpuid && +		    (cpu != cpu_first_thread_sibling(cpu) || +		     cpu == cpu_first_thread_sibling(boot_cpuid))) +			map = false; +#endif + +		if (map) +			linear_map_top = map_mem_in_cams(linear_map_top, +							 num_cams, false);  	}  #endif @@ -729,10 +746,14 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,  	 * entries are supported though that may eventually  	 * change.  	 * -	 * on FSL Embedded 64-bit, we adjust the RMA size to match the -	 * first bolted TLB entry size.  We still limit max to 1G even if -	 * the TLB could cover more.  This is due to what the early init -	 * code is setup to do. +	 * on FSL Embedded 64-bit, usually all RAM is bolted, but with +	 * unusual memory sizes it's possible for some RAM to not be mapped +	 * (such RAM is not used at all by Linux, since we don't support +	 * highmem on 64-bit).  We limit ppc64_rma_size to what would be +	 * mappable if this memblock is the only one.  Additional memblocks +	 * can only increase, not decrease, the amount that ends up getting +	 * mapped.  We still limit max to 1G even if we'll eventually map +	 * more.  This is due to what the early init code is set up to do.  	 *  	 * We crop it to the size of the first MEMBLOCK to  	 * avoid going over total available memory just in case... @@ -740,8 +761,14 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,  #ifdef CONFIG_PPC_FSL_BOOK3E  	if (mmu_has_feature(MMU_FTR_TYPE_FSL_E)) {  		unsigned long linear_sz; -		linear_sz = calc_cam_sz(first_memblock_size, PAGE_OFFSET, -					first_memblock_base); +		unsigned int num_cams; + +		/* use a quarter of the TLBCAM for bolted linear map */ +		num_cams = (mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY) / 4; + +		linear_sz = map_mem_in_cams(first_memblock_size, num_cams, +					    true); +  		ppc64_rma_size = min_t(u64, linear_sz, 0x40000000);  	} else  #endif diff --git a/arch/powerpc/mm/tlb_nohash_low.S b/arch/powerpc/mm/tlb_nohash_low.S index 43ff3c797fbf..68c477592e43 100644 --- a/arch/powerpc/mm/tlb_nohash_low.S +++ b/arch/powerpc/mm/tlb_nohash_low.S @@ -400,6 +400,7 @@ _GLOBAL(set_context)   * extern void loadcam_entry(unsigned int index)   *   * Load TLBCAM[index] entry in to the L2 CAM MMU + * Must preserve r7, r8, r9, and r10   */  _GLOBAL(loadcam_entry)  	mflr	r5 @@ -423,4 +424,66 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS)  	tlbwe  	isync  	blr + +/* + * Load multiple TLB entries at once, using an alternate-space + * trampoline so that we don't have to care about whether the same + * TLB entry maps us before and after. + * + * r3 = first entry to write + * r4 = number of entries to write + * r5 = temporary tlb entry + */ +_GLOBAL(loadcam_multi) +	mflr	r8 + +	/* +	 * Set up temporary TLB entry that is the same as what we're +	 * running from, but in AS=1. +	 */ +	bl	1f +1:	mflr	r6 +	tlbsx	0,r8 +	mfspr	r6,SPRN_MAS1 +	ori	r6,r6,MAS1_TS +	mtspr	SPRN_MAS1,r6 +	mfspr	r6,SPRN_MAS0 +	rlwimi	r6,r5,MAS0_ESEL_SHIFT,MAS0_ESEL_MASK +	mr	r7,r5 +	mtspr	SPRN_MAS0,r6 +	isync +	tlbwe +	isync + +	/* Switch to AS=1 */ +	mfmsr	r6 +	ori	r6,r6,MSR_IS|MSR_DS +	mtmsr	r6 +	isync + +	mr	r9,r3 +	add	r10,r3,r4 +2:	bl	loadcam_entry +	addi	r9,r9,1 +	cmpw	r9,r10 +	mr	r3,r9 +	blt	2b + +	/* Return to AS=0 and clear the temporary entry */ +	mfmsr	r6 +	rlwinm.	r6,r6,0,~(MSR_IS|MSR_DS) +	mtmsr	r6 +	isync + +	li	r6,0 +	mtspr	SPRN_MAS1,r6 +	rlwinm	r6,r7,MAS0_ESEL_SHIFT,MAS0_ESEL_MASK +	oris	r6,r6,MAS0_TLBSEL(1)@h +	mtspr	SPRN_MAS0,r6 +	isync +	tlbwe +	isync + +	mtlr	r8 +	blr  #endif |