Path: blob/master/arch/powerpc/mm/book3s64/hugetlbpage.c
26481 views
// SPDX-License-Identifier: GPL-2.01/*2* PPC64 Huge TLB Page Support for hash based MMUs (POWER4 and later)3*4* Copyright (C) 2003 David Gibson, IBM Corporation.5*6* Based on the IA-32 version:7* Copyright (C) 2002, Rohit Seth <[email protected]>8*/910#include <linux/mm.h>11#include <linux/hugetlb.h>12#include <asm/cacheflush.h>13#include <asm/machdep.h>1415unsigned int hpage_shift;16EXPORT_SYMBOL(hpage_shift);1718#ifdef CONFIG_PPC_64S_HASH_MMU19int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,20pte_t *ptep, unsigned long trap, unsigned long flags,21int ssize, unsigned int shift, unsigned int mmu_psize)22{23real_pte_t rpte;24unsigned long vpn;25unsigned long old_pte, new_pte;26unsigned long rflags, pa;27long slot, offset;2829BUG_ON(shift != mmu_psize_defs[mmu_psize].shift);3031/* Search the Linux page table for a match with va */32vpn = hpt_vpn(ea, vsid, ssize);3334/*35* At this point, we have a pte (old_pte) which can be used to build36* or update an HPTE. There are 2 cases:37*38* 1. There is a valid (present) pte with no associated HPTE (this is39* the most common case)40* 2. There is a valid (present) pte with an associated HPTE. The41* current values of the pp bits in the HPTE prevent access42* because we are doing software DIRTY bit management and the43* page is currently not DIRTY.44*/454647do {48old_pte = pte_val(*ptep);49/* If PTE busy, retry the access */50if (unlikely(old_pte & H_PAGE_BUSY))51return 0;52/* If PTE permissions don't match, take page fault */53if (unlikely(!check_pte_access(access, old_pte)))54return 1;55/*56* If hash-4k, hugepages use seeral contiguous PxD entries57* so bail out and let mm make the page young or dirty58*/59if (IS_ENABLED(CONFIG_PPC_4K_PAGES)) {60if (!(old_pte & _PAGE_ACCESSED))61return 1;62if ((access & _PAGE_WRITE) && !(old_pte & _PAGE_DIRTY))63return 1;64}6566/*67* Try to lock the PTE, add ACCESSED and DIRTY if it was68* a write access69*/70new_pte = old_pte | H_PAGE_BUSY | _PAGE_ACCESSED;71if (access & _PAGE_WRITE)72new_pte |= _PAGE_DIRTY;73} while(!pte_xchg(ptep, __pte(old_pte), __pte(new_pte)));7475/* Make sure this is a hugetlb entry */76if (old_pte & H_PAGE_THP_HUGE)77return 0;7879rflags = htab_convert_pte_flags(new_pte, flags);80if (unlikely(mmu_psize == MMU_PAGE_16G))81offset = PTRS_PER_PUD;82else83offset = PTRS_PER_PMD;84rpte = __real_pte(__pte(old_pte), ptep, offset);8586if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE))87/*88* No CPU has hugepages but lacks no execute, so we89* don't need to worry about that case90*/91rflags = hash_page_do_lazy_icache(rflags, __pte(old_pte), trap);9293/* Check if pte already has an hpte (case 2) */94if (unlikely(old_pte & H_PAGE_HASHPTE)) {95/* There MIGHT be an HPTE for this pte */96unsigned long gslot;9798gslot = pte_get_hash_gslot(vpn, shift, ssize, rpte, 0);99if (mmu_hash_ops.hpte_updatepp(gslot, rflags, vpn, mmu_psize,100mmu_psize, ssize, flags) == -1)101old_pte &= ~_PAGE_HPTEFLAGS;102}103104if (likely(!(old_pte & H_PAGE_HASHPTE))) {105unsigned long hash = hpt_hash(vpn, shift, ssize);106107pa = pte_pfn(__pte(old_pte)) << PAGE_SHIFT;108109/* clear HPTE slot informations in new PTE */110new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | H_PAGE_HASHPTE;111112slot = hpte_insert_repeating(hash, vpn, pa, rflags, 0,113mmu_psize, ssize);114115/*116* Hypervisor failure. Restore old pte and return -1117* similar to __hash_page_*118*/119if (unlikely(slot == -2)) {120*ptep = __pte(old_pte);121hash_failure_debug(ea, access, vsid, trap, ssize,122mmu_psize, mmu_psize, old_pte);123return -1;124}125126new_pte |= pte_set_hidx(ptep, rpte, 0, slot, offset);127}128129/*130* No need to use ldarx/stdcx here131*/132*ptep = __pte(new_pte & ~H_PAGE_BUSY);133return 0;134}135#endif136137pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,138unsigned long addr, pte_t *ptep)139{140unsigned long pte_val;141/*142* Clear the _PAGE_PRESENT so that no hardware parallel update is143* possible. Also keep the pte_present true so that we don't take144* wrong fault.145*/146pte_val = pte_update(vma->vm_mm, addr, ptep,147_PAGE_PRESENT, _PAGE_INVALID, 1);148149return __pte(pte_val);150}151152void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,153pte_t *ptep, pte_t old_pte, pte_t pte)154{155unsigned long psize;156157if (radix_enabled())158return radix__huge_ptep_modify_prot_commit(vma, addr, ptep,159old_pte, pte);160161psize = huge_page_size(hstate_vma(vma));162set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize);163}164165void __init hugetlbpage_init_defaultsize(void)166{167/* Set default large page size. Currently, we pick 16M or 1M168* depending on what is available169*/170if (mmu_psize_defs[MMU_PAGE_16M].shift)171hpage_shift = mmu_psize_defs[MMU_PAGE_16M].shift;172else if (mmu_psize_defs[MMU_PAGE_1M].shift)173hpage_shift = mmu_psize_defs[MMU_PAGE_1M].shift;174else if (mmu_psize_defs[MMU_PAGE_2M].shift)175hpage_shift = mmu_psize_defs[MMU_PAGE_2M].shift;176}177178179