162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* arch/sparc64/mm/tsb.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/preempt.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/mm_types.h> 1162306a36Sopenharmony_ci#include <linux/pgtable.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/page.h> 1462306a36Sopenharmony_ci#include <asm/mmu_context.h> 1562306a36Sopenharmony_ci#include <asm/setup.h> 1662306a36Sopenharmony_ci#include <asm/tsb.h> 1762306a36Sopenharmony_ci#include <asm/tlb.h> 1862306a36Sopenharmony_ci#include <asm/oplib.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciextern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES]; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic inline unsigned long tsb_hash(unsigned long vaddr, unsigned long hash_shift, unsigned long nentries) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci vaddr >>= hash_shift; 2562306a36Sopenharmony_ci return vaddr & (nentries - 1); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline int tag_compare(unsigned long tag, unsigned long vaddr) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return (tag == (vaddr >> 22)); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void flush_tsb_kernel_range_scan(unsigned long start, unsigned long end) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned long idx; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (idx = 0; idx < KERNEL_TSB_NENTRIES; idx++) { 3862306a36Sopenharmony_ci struct tsb *ent = &swapper_tsb[idx]; 3962306a36Sopenharmony_ci unsigned long match = idx << 13; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci match |= (ent->tag << 22); 4262306a36Sopenharmony_ci if (match >= start && match < end) 4362306a36Sopenharmony_ci ent->tag = (1UL << TSB_TAG_INVALID_BIT); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* TSB flushes need only occur on the processor initiating the address 4862306a36Sopenharmony_ci * space modification, not on each cpu the address space has run on. 4962306a36Sopenharmony_ci * Only the TLB flush needs that treatment. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid flush_tsb_kernel_range(unsigned long start, unsigned long end) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned long v; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if ((end - start) >> PAGE_SHIFT >= 2 * KERNEL_TSB_NENTRIES) 5762306a36Sopenharmony_ci return flush_tsb_kernel_range_scan(start, end); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (v = start; v < end; v += PAGE_SIZE) { 6062306a36Sopenharmony_ci unsigned long hash = tsb_hash(v, PAGE_SHIFT, 6162306a36Sopenharmony_ci KERNEL_TSB_NENTRIES); 6262306a36Sopenharmony_ci struct tsb *ent = &swapper_tsb[hash]; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (tag_compare(ent->tag, v)) 6562306a36Sopenharmony_ci ent->tag = (1UL << TSB_TAG_INVALID_BIT); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __flush_tsb_one_entry(unsigned long tsb, unsigned long v, 7062306a36Sopenharmony_ci unsigned long hash_shift, 7162306a36Sopenharmony_ci unsigned long nentries) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long tag, ent, hash; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci v &= ~0x1UL; 7662306a36Sopenharmony_ci hash = tsb_hash(v, hash_shift, nentries); 7762306a36Sopenharmony_ci ent = tsb + (hash * sizeof(struct tsb)); 7862306a36Sopenharmony_ci tag = (v >> 22UL); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tsb_flush(ent, tag); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void __flush_tsb_one(struct tlb_batch *tb, unsigned long hash_shift, 8462306a36Sopenharmony_ci unsigned long tsb, unsigned long nentries) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci unsigned long i; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (i = 0; i < tb->tlb_nr; i++) 8962306a36Sopenharmony_ci __flush_tsb_one_entry(tsb, tb->vaddrs[i], hash_shift, nentries); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 9362306a36Sopenharmony_cistatic void __flush_huge_tsb_one_entry(unsigned long tsb, unsigned long v, 9462306a36Sopenharmony_ci unsigned long hash_shift, 9562306a36Sopenharmony_ci unsigned long nentries, 9662306a36Sopenharmony_ci unsigned int hugepage_shift) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci unsigned int hpage_entries; 9962306a36Sopenharmony_ci unsigned int i; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci hpage_entries = 1 << (hugepage_shift - hash_shift); 10262306a36Sopenharmony_ci for (i = 0; i < hpage_entries; i++) 10362306a36Sopenharmony_ci __flush_tsb_one_entry(tsb, v + (i << hash_shift), hash_shift, 10462306a36Sopenharmony_ci nentries); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void __flush_huge_tsb_one(struct tlb_batch *tb, unsigned long hash_shift, 10862306a36Sopenharmony_ci unsigned long tsb, unsigned long nentries, 10962306a36Sopenharmony_ci unsigned int hugepage_shift) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci unsigned long i; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (i = 0; i < tb->tlb_nr; i++) 11462306a36Sopenharmony_ci __flush_huge_tsb_one_entry(tsb, tb->vaddrs[i], hash_shift, 11562306a36Sopenharmony_ci nentries, hugepage_shift); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci#endif 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid flush_tsb_user(struct tlb_batch *tb) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct mm_struct *mm = tb->mm; 12262306a36Sopenharmony_ci unsigned long nentries, base, flags; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock_irqsave(&mm->context.lock, flags); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (tb->hugepage_shift < REAL_HPAGE_SHIFT) { 12762306a36Sopenharmony_ci base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb; 12862306a36Sopenharmony_ci nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries; 12962306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) 13062306a36Sopenharmony_ci base = __pa(base); 13162306a36Sopenharmony_ci if (tb->hugepage_shift == PAGE_SHIFT) 13262306a36Sopenharmony_ci __flush_tsb_one(tb, PAGE_SHIFT, base, nentries); 13362306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci __flush_huge_tsb_one(tb, PAGE_SHIFT, base, nentries, 13662306a36Sopenharmony_ci tb->hugepage_shift); 13762306a36Sopenharmony_ci#endif 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 14062306a36Sopenharmony_ci else if (mm->context.tsb_block[MM_TSB_HUGE].tsb) { 14162306a36Sopenharmony_ci base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb; 14262306a36Sopenharmony_ci nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries; 14362306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) 14462306a36Sopenharmony_ci base = __pa(base); 14562306a36Sopenharmony_ci __flush_huge_tsb_one(tb, REAL_HPAGE_SHIFT, base, nentries, 14662306a36Sopenharmony_ci tb->hugepage_shift); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci spin_unlock_irqrestore(&mm->context.lock, flags); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_civoid flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr, 15362306a36Sopenharmony_ci unsigned int hugepage_shift) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci unsigned long nentries, base, flags; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&mm->context.lock, flags); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (hugepage_shift < REAL_HPAGE_SHIFT) { 16062306a36Sopenharmony_ci base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb; 16162306a36Sopenharmony_ci nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries; 16262306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) 16362306a36Sopenharmony_ci base = __pa(base); 16462306a36Sopenharmony_ci if (hugepage_shift == PAGE_SHIFT) 16562306a36Sopenharmony_ci __flush_tsb_one_entry(base, vaddr, PAGE_SHIFT, 16662306a36Sopenharmony_ci nentries); 16762306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci __flush_huge_tsb_one_entry(base, vaddr, PAGE_SHIFT, 17062306a36Sopenharmony_ci nentries, hugepage_shift); 17162306a36Sopenharmony_ci#endif 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 17462306a36Sopenharmony_ci else if (mm->context.tsb_block[MM_TSB_HUGE].tsb) { 17562306a36Sopenharmony_ci base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb; 17662306a36Sopenharmony_ci nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries; 17762306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) 17862306a36Sopenharmony_ci base = __pa(base); 17962306a36Sopenharmony_ci __flush_huge_tsb_one_entry(base, vaddr, REAL_HPAGE_SHIFT, 18062306a36Sopenharmony_ci nentries, hugepage_shift); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&mm->context.lock, flags); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_8K 18762306a36Sopenharmony_ci#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_8K 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 19062306a36Sopenharmony_ci#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_4MB 19162306a36Sopenharmony_ci#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_4MB 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigned long tsb_bytes) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci unsigned long tsb_reg, base, tsb_paddr; 19762306a36Sopenharmony_ci unsigned long page_sz, tte; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_nentries = 20062306a36Sopenharmony_ci tsb_bytes / sizeof(struct tsb); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci switch (tsb_idx) { 20362306a36Sopenharmony_ci case MM_TSB_BASE: 20462306a36Sopenharmony_ci base = TSBMAP_8K_BASE; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 20762306a36Sopenharmony_ci case MM_TSB_HUGE: 20862306a36Sopenharmony_ci base = TSBMAP_4M_BASE; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci#endif 21162306a36Sopenharmony_ci default: 21262306a36Sopenharmony_ci BUG(); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci tte = pgprot_val(PAGE_KERNEL_LOCKED); 21662306a36Sopenharmony_ci tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb); 21762306a36Sopenharmony_ci BUG_ON(tsb_paddr & (tsb_bytes - 1UL)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Use the smallest page size that can map the whole TSB 22062306a36Sopenharmony_ci * in one TLB entry. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci switch (tsb_bytes) { 22362306a36Sopenharmony_ci case 8192 << 0: 22462306a36Sopenharmony_ci tsb_reg = 0x0UL; 22562306a36Sopenharmony_ci#ifdef DCACHE_ALIASING_POSSIBLE 22662306a36Sopenharmony_ci base += (tsb_paddr & 8192); 22762306a36Sopenharmony_ci#endif 22862306a36Sopenharmony_ci page_sz = 8192; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci case 8192 << 1: 23262306a36Sopenharmony_ci tsb_reg = 0x1UL; 23362306a36Sopenharmony_ci page_sz = 64 * 1024; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci case 8192 << 2: 23762306a36Sopenharmony_ci tsb_reg = 0x2UL; 23862306a36Sopenharmony_ci page_sz = 64 * 1024; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci case 8192 << 3: 24262306a36Sopenharmony_ci tsb_reg = 0x3UL; 24362306a36Sopenharmony_ci page_sz = 64 * 1024; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci case 8192 << 4: 24762306a36Sopenharmony_ci tsb_reg = 0x4UL; 24862306a36Sopenharmony_ci page_sz = 512 * 1024; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci case 8192 << 5: 25262306a36Sopenharmony_ci tsb_reg = 0x5UL; 25362306a36Sopenharmony_ci page_sz = 512 * 1024; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci case 8192 << 6: 25762306a36Sopenharmony_ci tsb_reg = 0x6UL; 25862306a36Sopenharmony_ci page_sz = 512 * 1024; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci case 8192 << 7: 26262306a36Sopenharmony_ci tsb_reg = 0x7UL; 26362306a36Sopenharmony_ci page_sz = 4 * 1024 * 1024; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci default: 26762306a36Sopenharmony_ci printk(KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n", 26862306a36Sopenharmony_ci current->comm, current->pid, tsb_bytes); 26962306a36Sopenharmony_ci BUG(); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci tte |= pte_sz_bits(page_sz); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) { 27462306a36Sopenharmony_ci /* Physical mapping, no locked TLB entry for TSB. */ 27562306a36Sopenharmony_ci tsb_reg |= tsb_paddr; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg; 27862306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_map_vaddr = 0; 27962306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_map_pte = 0; 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci tsb_reg |= base; 28262306a36Sopenharmony_ci tsb_reg |= (tsb_paddr & (page_sz - 1UL)); 28362306a36Sopenharmony_ci tte |= (tsb_paddr & ~(page_sz - 1UL)); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg; 28662306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_map_vaddr = base; 28762306a36Sopenharmony_ci mm->context.tsb_block[tsb_idx].tsb_map_pte = tte; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Setup the Hypervisor TSB descriptor. */ 29162306a36Sopenharmony_ci if (tlb_type == hypervisor) { 29262306a36Sopenharmony_ci struct hv_tsb_descr *hp = &mm->context.tsb_descr[tsb_idx]; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci switch (tsb_idx) { 29562306a36Sopenharmony_ci case MM_TSB_BASE: 29662306a36Sopenharmony_ci hp->pgsz_idx = HV_PGSZ_IDX_BASE; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 29962306a36Sopenharmony_ci case MM_TSB_HUGE: 30062306a36Sopenharmony_ci hp->pgsz_idx = HV_PGSZ_IDX_HUGE; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci#endif 30362306a36Sopenharmony_ci default: 30462306a36Sopenharmony_ci BUG(); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci hp->assoc = 1; 30762306a36Sopenharmony_ci hp->num_ttes = tsb_bytes / 16; 30862306a36Sopenharmony_ci hp->ctx_idx = 0; 30962306a36Sopenharmony_ci switch (tsb_idx) { 31062306a36Sopenharmony_ci case MM_TSB_BASE: 31162306a36Sopenharmony_ci hp->pgsz_mask = HV_PGSZ_MASK_BASE; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 31462306a36Sopenharmony_ci case MM_TSB_HUGE: 31562306a36Sopenharmony_ci hp->pgsz_mask = HV_PGSZ_MASK_HUGE; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci#endif 31862306a36Sopenharmony_ci default: 31962306a36Sopenharmony_ci BUG(); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci hp->tsb_base = tsb_paddr; 32262306a36Sopenharmony_ci hp->resv = 0; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistruct kmem_cache *pgtable_cache __read_mostly; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic struct kmem_cache *tsb_caches[8] __read_mostly; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const char *tsb_cache_names[8] = { 33162306a36Sopenharmony_ci "tsb_8KB", 33262306a36Sopenharmony_ci "tsb_16KB", 33362306a36Sopenharmony_ci "tsb_32KB", 33462306a36Sopenharmony_ci "tsb_64KB", 33562306a36Sopenharmony_ci "tsb_128KB", 33662306a36Sopenharmony_ci "tsb_256KB", 33762306a36Sopenharmony_ci "tsb_512KB", 33862306a36Sopenharmony_ci "tsb_1MB", 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_civoid __init pgtable_cache_init(void) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci unsigned long i; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pgtable_cache = kmem_cache_create("pgtable_cache", 34662306a36Sopenharmony_ci PAGE_SIZE, PAGE_SIZE, 34762306a36Sopenharmony_ci 0, 34862306a36Sopenharmony_ci _clear_page); 34962306a36Sopenharmony_ci if (!pgtable_cache) { 35062306a36Sopenharmony_ci prom_printf("pgtable_cache_init(): Could not create!\n"); 35162306a36Sopenharmony_ci prom_halt(); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tsb_cache_names); i++) { 35562306a36Sopenharmony_ci unsigned long size = 8192 << i; 35662306a36Sopenharmony_ci const char *name = tsb_cache_names[i]; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci tsb_caches[i] = kmem_cache_create(name, 35962306a36Sopenharmony_ci size, size, 36062306a36Sopenharmony_ci 0, NULL); 36162306a36Sopenharmony_ci if (!tsb_caches[i]) { 36262306a36Sopenharmony_ci prom_printf("Could not create %s cache\n", name); 36362306a36Sopenharmony_ci prom_halt(); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciint sysctl_tsb_ratio = -2; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic unsigned long tsb_size_to_rss_limit(unsigned long new_size) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci unsigned long num_ents = (new_size / sizeof(struct tsb)); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (sysctl_tsb_ratio < 0) 37562306a36Sopenharmony_ci return num_ents - (num_ents >> -sysctl_tsb_ratio); 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci return num_ents + (num_ents >> sysctl_tsb_ratio); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* When the RSS of an address space exceeds tsb_rss_limit for a TSB, 38162306a36Sopenharmony_ci * do_sparc64_fault() invokes this routine to try and grow it. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * When we reach the maximum TSB size supported, we stick ~0UL into 38462306a36Sopenharmony_ci * tsb_rss_limit for that TSB so the grow checks in do_sparc64_fault() 38562306a36Sopenharmony_ci * will not trigger any longer. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * The TSB can be anywhere from 8K to 1MB in size, in increasing powers 38862306a36Sopenharmony_ci * of two. The TSB must be aligned to it's size, so f.e. a 512K TSB 38962306a36Sopenharmony_ci * must be 512K aligned. It also must be physically contiguous, so we 39062306a36Sopenharmony_ci * cannot use vmalloc(). 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * The idea here is to grow the TSB when the RSS of the process approaches 39362306a36Sopenharmony_ci * the number of entries that the current TSB can hold at once. Currently, 39462306a36Sopenharmony_ci * we trigger when the RSS hits 3/4 of the TSB capacity. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_civoid tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci unsigned long max_tsb_size = 1 * 1024 * 1024; 39962306a36Sopenharmony_ci unsigned long new_size, old_size, flags; 40062306a36Sopenharmony_ci struct tsb *old_tsb, *new_tsb; 40162306a36Sopenharmony_ci unsigned long new_cache_index, old_cache_index; 40262306a36Sopenharmony_ci unsigned long new_rss_limit; 40362306a36Sopenharmony_ci gfp_t gfp_flags; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (max_tsb_size > PAGE_SIZE << MAX_ORDER) 40662306a36Sopenharmony_ci max_tsb_size = PAGE_SIZE << MAX_ORDER; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci new_cache_index = 0; 40962306a36Sopenharmony_ci for (new_size = 8192; new_size < max_tsb_size; new_size <<= 1UL) { 41062306a36Sopenharmony_ci new_rss_limit = tsb_size_to_rss_limit(new_size); 41162306a36Sopenharmony_ci if (new_rss_limit > rss) 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci new_cache_index++; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (new_size == max_tsb_size) 41762306a36Sopenharmony_ci new_rss_limit = ~0UL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciretry_tsb_alloc: 42062306a36Sopenharmony_ci gfp_flags = GFP_KERNEL; 42162306a36Sopenharmony_ci if (new_size > (PAGE_SIZE * 2)) 42262306a36Sopenharmony_ci gfp_flags |= __GFP_NOWARN | __GFP_NORETRY; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index], 42562306a36Sopenharmony_ci gfp_flags, numa_node_id()); 42662306a36Sopenharmony_ci if (unlikely(!new_tsb)) { 42762306a36Sopenharmony_ci /* Not being able to fork due to a high-order TSB 42862306a36Sopenharmony_ci * allocation failure is very bad behavior. Just back 42962306a36Sopenharmony_ci * down to a 0-order allocation and force no TSB 43062306a36Sopenharmony_ci * growing for this address space. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci if (mm->context.tsb_block[tsb_index].tsb == NULL && 43362306a36Sopenharmony_ci new_cache_index > 0) { 43462306a36Sopenharmony_ci new_cache_index = 0; 43562306a36Sopenharmony_ci new_size = 8192; 43662306a36Sopenharmony_ci new_rss_limit = ~0UL; 43762306a36Sopenharmony_ci goto retry_tsb_alloc; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* If we failed on a TSB grow, we are under serious 44162306a36Sopenharmony_ci * memory pressure so don't try to grow any more. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci if (mm->context.tsb_block[tsb_index].tsb != NULL) 44462306a36Sopenharmony_ci mm->context.tsb_block[tsb_index].tsb_rss_limit = ~0UL; 44562306a36Sopenharmony_ci return; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Mark all tags as invalid. */ 44962306a36Sopenharmony_ci tsb_init(new_tsb, new_size); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Ok, we are about to commit the changes. If we are 45262306a36Sopenharmony_ci * growing an existing TSB the locking is very tricky, 45362306a36Sopenharmony_ci * so WATCH OUT! 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * We have to hold mm->context.lock while committing to the 45662306a36Sopenharmony_ci * new TSB, this synchronizes us with processors in 45762306a36Sopenharmony_ci * flush_tsb_user() and switch_mm() for this address space. 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * But even with that lock held, processors run asynchronously 46062306a36Sopenharmony_ci * accessing the old TSB via TLB miss handling. This is OK 46162306a36Sopenharmony_ci * because those actions are just propagating state from the 46262306a36Sopenharmony_ci * Linux page tables into the TSB, page table mappings are not 46362306a36Sopenharmony_ci * being changed. If a real fault occurs, the processor will 46462306a36Sopenharmony_ci * synchronize with us when it hits flush_tsb_user(), this is 46562306a36Sopenharmony_ci * also true for the case where vmscan is modifying the page 46662306a36Sopenharmony_ci * tables. The only thing we need to be careful with is to 46762306a36Sopenharmony_ci * skip any locked TSB entries during copy_tsb(). 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * When we finish committing to the new TSB, we have to drop 47062306a36Sopenharmony_ci * the lock and ask all other cpus running this address space 47162306a36Sopenharmony_ci * to run tsb_context_switch() to see the new TSB table. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci spin_lock_irqsave(&mm->context.lock, flags); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci old_tsb = mm->context.tsb_block[tsb_index].tsb; 47662306a36Sopenharmony_ci old_cache_index = 47762306a36Sopenharmony_ci (mm->context.tsb_block[tsb_index].tsb_reg_val & 0x7UL); 47862306a36Sopenharmony_ci old_size = (mm->context.tsb_block[tsb_index].tsb_nentries * 47962306a36Sopenharmony_ci sizeof(struct tsb)); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Handle multiple threads trying to grow the TSB at the same time. 48362306a36Sopenharmony_ci * One will get in here first, and bump the size and the RSS limit. 48462306a36Sopenharmony_ci * The others will get in here next and hit this check. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci if (unlikely(old_tsb && 48762306a36Sopenharmony_ci (rss < mm->context.tsb_block[tsb_index].tsb_rss_limit))) { 48862306a36Sopenharmony_ci spin_unlock_irqrestore(&mm->context.lock, flags); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci kmem_cache_free(tsb_caches[new_cache_index], new_tsb); 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci mm->context.tsb_block[tsb_index].tsb_rss_limit = new_rss_limit; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (old_tsb) { 49762306a36Sopenharmony_ci extern void copy_tsb(unsigned long old_tsb_base, 49862306a36Sopenharmony_ci unsigned long old_tsb_size, 49962306a36Sopenharmony_ci unsigned long new_tsb_base, 50062306a36Sopenharmony_ci unsigned long new_tsb_size, 50162306a36Sopenharmony_ci unsigned long page_size_shift); 50262306a36Sopenharmony_ci unsigned long old_tsb_base = (unsigned long) old_tsb; 50362306a36Sopenharmony_ci unsigned long new_tsb_base = (unsigned long) new_tsb; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (tlb_type == cheetah_plus || tlb_type == hypervisor) { 50662306a36Sopenharmony_ci old_tsb_base = __pa(old_tsb_base); 50762306a36Sopenharmony_ci new_tsb_base = __pa(new_tsb_base); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci copy_tsb(old_tsb_base, old_size, new_tsb_base, new_size, 51062306a36Sopenharmony_ci tsb_index == MM_TSB_BASE ? 51162306a36Sopenharmony_ci PAGE_SHIFT : REAL_HPAGE_SHIFT); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mm->context.tsb_block[tsb_index].tsb = new_tsb; 51562306a36Sopenharmony_ci setup_tsb_params(mm, tsb_index, new_size); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci spin_unlock_irqrestore(&mm->context.lock, flags); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* If old_tsb is NULL, we're being invoked for the first time 52062306a36Sopenharmony_ci * from init_new_context(). 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if (old_tsb) { 52362306a36Sopenharmony_ci /* Reload it on the local cpu. */ 52462306a36Sopenharmony_ci tsb_context_switch(mm); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* Now force other processors to do the same. */ 52762306a36Sopenharmony_ci preempt_disable(); 52862306a36Sopenharmony_ci smp_tsb_sync(mm); 52962306a36Sopenharmony_ci preempt_enable(); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Now it is safe to free the old tsb. */ 53262306a36Sopenharmony_ci kmem_cache_free(tsb_caches[old_cache_index], old_tsb); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ciint init_new_context(struct task_struct *tsk, struct mm_struct *mm) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci unsigned long mm_rss = get_mm_rss(mm); 53962306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 54062306a36Sopenharmony_ci unsigned long saved_hugetlb_pte_count; 54162306a36Sopenharmony_ci unsigned long saved_thp_pte_count; 54262306a36Sopenharmony_ci#endif 54362306a36Sopenharmony_ci unsigned int i; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_lock_init(&mm->context.lock); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci mm->context.sparc64_ctx_val = 0UL; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci mm->context.tag_store = NULL; 55062306a36Sopenharmony_ci spin_lock_init(&mm->context.tag_lock); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 55362306a36Sopenharmony_ci /* We reset them to zero because the fork() page copying 55462306a36Sopenharmony_ci * will re-increment the counters as the parent PTEs are 55562306a36Sopenharmony_ci * copied into the child address space. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci saved_hugetlb_pte_count = mm->context.hugetlb_pte_count; 55862306a36Sopenharmony_ci saved_thp_pte_count = mm->context.thp_pte_count; 55962306a36Sopenharmony_ci mm->context.hugetlb_pte_count = 0; 56062306a36Sopenharmony_ci mm->context.thp_pte_count = 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci mm_rss -= saved_thp_pte_count * (HPAGE_SIZE / PAGE_SIZE); 56362306a36Sopenharmony_ci#endif 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* copy_mm() copies over the parent's mm_struct before calling 56662306a36Sopenharmony_ci * us, so we need to zero out the TSB pointer or else tsb_grow() 56762306a36Sopenharmony_ci * will be confused and think there is an older TSB to free up. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci for (i = 0; i < MM_NUM_TSBS; i++) 57062306a36Sopenharmony_ci mm->context.tsb_block[i].tsb = NULL; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* If this is fork, inherit the parent's TSB size. We would 57362306a36Sopenharmony_ci * grow it to that size on the first page fault anyways. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci tsb_grow(mm, MM_TSB_BASE, mm_rss); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 57862306a36Sopenharmony_ci if (unlikely(saved_hugetlb_pte_count + saved_thp_pte_count)) 57962306a36Sopenharmony_ci tsb_grow(mm, MM_TSB_HUGE, 58062306a36Sopenharmony_ci (saved_hugetlb_pte_count + saved_thp_pte_count) * 58162306a36Sopenharmony_ci REAL_HPAGE_PER_HPAGE); 58262306a36Sopenharmony_ci#endif 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb)) 58562306a36Sopenharmony_ci return -ENOMEM; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void tsb_destroy_one(struct tsb_config *tp) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci unsigned long cache_index; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (!tp->tsb) 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci cache_index = tp->tsb_reg_val & 0x7UL; 59762306a36Sopenharmony_ci kmem_cache_free(tsb_caches[cache_index], tp->tsb); 59862306a36Sopenharmony_ci tp->tsb = NULL; 59962306a36Sopenharmony_ci tp->tsb_reg_val = 0UL; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_civoid destroy_context(struct mm_struct *mm) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci unsigned long flags, i; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci for (i = 0; i < MM_NUM_TSBS; i++) 60762306a36Sopenharmony_ci tsb_destroy_one(&mm->context.tsb_block[i]); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci spin_lock_irqsave(&ctx_alloc_lock, flags); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (CTX_VALID(mm->context)) { 61262306a36Sopenharmony_ci unsigned long nr = CTX_NRBITS(mm->context); 61362306a36Sopenharmony_ci mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63)); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx_alloc_lock, flags); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* If ADI tag storage was allocated for this task, free it */ 61962306a36Sopenharmony_ci if (mm->context.tag_store) { 62062306a36Sopenharmony_ci tag_storage_desc_t *tag_desc; 62162306a36Sopenharmony_ci unsigned long max_desc; 62262306a36Sopenharmony_ci unsigned char *tags; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci tag_desc = mm->context.tag_store; 62562306a36Sopenharmony_ci max_desc = PAGE_SIZE/sizeof(tag_storage_desc_t); 62662306a36Sopenharmony_ci for (i = 0; i < max_desc; i++) { 62762306a36Sopenharmony_ci tags = tag_desc->tags; 62862306a36Sopenharmony_ci tag_desc->tags = NULL; 62962306a36Sopenharmony_ci kfree(tags); 63062306a36Sopenharmony_ci tag_desc++; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci kfree(mm->context.tag_store); 63362306a36Sopenharmony_ci mm->context.tag_store = NULL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci} 636