| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright IBM Corp. 2007, 2011
- * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
- */
- #include <linux/cpufeature.h>
- #include <linux/export.h>
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/gfp.h>
- #include <linux/mm.h>
- #include <linux/swap.h>
- #include <linux/smp.h>
- #include <linux/spinlock.h>
- #include <linux/rcupdate.h>
- #include <linux/slab.h>
- #include <linux/leafops.h>
- #include <linux/sysctl.h>
- #include <linux/ksm.h>
- #include <linux/mman.h>
- #include <asm/tlbflush.h>
- #include <asm/mmu_context.h>
- #include <asm/page-states.h>
- #include <asm/machine.h>
- pgprot_t pgprot_writecombine(pgprot_t prot)
- {
- /*
- * mio_wb_bit_mask may be set on a different CPU, but it is only set
- * once at init and only read afterwards.
- */
- return __pgprot(pgprot_val(prot) | mio_wb_bit_mask);
- }
- EXPORT_SYMBOL_GPL(pgprot_writecombine);
- static inline void ptep_ipte_local(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, int nodat)
- {
- unsigned long opt, asce;
- if (machine_has_tlb_guest()) {
- opt = 0;
- asce = READ_ONCE(mm->context.gmap_asce);
- if (asce == 0UL || nodat)
- opt |= IPTE_NODAT;
- if (asce != -1UL) {
- asce = asce ? : mm->context.asce;
- opt |= IPTE_GUEST_ASCE;
- }
- __ptep_ipte(addr, ptep, opt, asce, IPTE_LOCAL);
- } else {
- __ptep_ipte(addr, ptep, 0, 0, IPTE_LOCAL);
- }
- }
- static inline void ptep_ipte_global(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, int nodat)
- {
- unsigned long opt, asce;
- if (machine_has_tlb_guest()) {
- opt = 0;
- asce = READ_ONCE(mm->context.gmap_asce);
- if (asce == 0UL || nodat)
- opt |= IPTE_NODAT;
- if (asce != -1UL) {
- asce = asce ? : mm->context.asce;
- opt |= IPTE_GUEST_ASCE;
- }
- __ptep_ipte(addr, ptep, opt, asce, IPTE_GLOBAL);
- } else {
- __ptep_ipte(addr, ptep, 0, 0, IPTE_GLOBAL);
- }
- }
- static inline pte_t ptep_flush_direct(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
- int nodat)
- {
- pte_t old;
- old = *ptep;
- if (unlikely(pte_val(old) & _PAGE_INVALID))
- return old;
- atomic_inc(&mm->context.flush_count);
- if (cpu_has_tlb_lc() &&
- cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
- ptep_ipte_local(mm, addr, ptep, nodat);
- else
- ptep_ipte_global(mm, addr, ptep, nodat);
- atomic_dec(&mm->context.flush_count);
- return old;
- }
- static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep,
- int nodat)
- {
- pte_t old;
- old = *ptep;
- if (unlikely(pte_val(old) & _PAGE_INVALID))
- return old;
- atomic_inc(&mm->context.flush_count);
- if (cpumask_equal(&mm->context.cpu_attach_mask,
- cpumask_of(smp_processor_id()))) {
- set_pte(ptep, set_pte_bit(*ptep, __pgprot(_PAGE_INVALID)));
- mm->context.flush_mm = 1;
- } else
- ptep_ipte_global(mm, addr, ptep, nodat);
- atomic_dec(&mm->context.flush_count);
- return old;
- }
- pte_t ptep_xchg_direct(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t new)
- {
- pte_t old;
- preempt_disable();
- old = ptep_flush_direct(mm, addr, ptep, 1);
- set_pte(ptep, new);
- preempt_enable();
- return old;
- }
- EXPORT_SYMBOL(ptep_xchg_direct);
- /*
- * Caller must check that new PTE only differs in _PAGE_PROTECT HW bit, so that
- * RDP can be used instead of IPTE. See also comments at pte_allow_rdp().
- */
- void ptep_reset_dat_prot(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
- pte_t new)
- {
- preempt_disable();
- atomic_inc(&mm->context.flush_count);
- if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
- __ptep_rdp(addr, ptep, 1);
- else
- __ptep_rdp(addr, ptep, 0);
- /*
- * PTE is not invalidated by RDP, only _PAGE_PROTECT is cleared. That
- * means it is still valid and active, and must not be changed according
- * to the architecture. But writing a new value that only differs in SW
- * bits is allowed.
- */
- set_pte(ptep, new);
- atomic_dec(&mm->context.flush_count);
- preempt_enable();
- }
- EXPORT_SYMBOL(ptep_reset_dat_prot);
- pte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t new)
- {
- pte_t old;
- preempt_disable();
- old = ptep_flush_lazy(mm, addr, ptep, 1);
- set_pte(ptep, new);
- preempt_enable();
- return old;
- }
- EXPORT_SYMBOL(ptep_xchg_lazy);
- pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr,
- pte_t *ptep)
- {
- return ptep_flush_lazy(vma->vm_mm, addr, ptep, 1);
- }
- void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
- pte_t *ptep, pte_t old_pte, pte_t pte)
- {
- set_pte(ptep, pte);
- }
- static inline void pmdp_idte_local(struct mm_struct *mm,
- unsigned long addr, pmd_t *pmdp)
- {
- if (machine_has_tlb_guest())
- __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE, mm->context.asce, IDTE_LOCAL);
- else
- __pmdp_idte(addr, pmdp, 0, 0, IDTE_LOCAL);
- }
- static inline void pmdp_idte_global(struct mm_struct *mm,
- unsigned long addr, pmd_t *pmdp)
- {
- if (machine_has_tlb_guest()) {
- __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE,
- mm->context.asce, IDTE_GLOBAL);
- } else {
- __pmdp_idte(addr, pmdp, 0, 0, IDTE_GLOBAL);
- }
- }
- static inline pmd_t pmdp_flush_direct(struct mm_struct *mm,
- unsigned long addr, pmd_t *pmdp)
- {
- pmd_t old;
- old = *pmdp;
- if (pmd_val(old) & _SEGMENT_ENTRY_INVALID)
- return old;
- atomic_inc(&mm->context.flush_count);
- if (cpu_has_tlb_lc() &&
- cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
- pmdp_idte_local(mm, addr, pmdp);
- else
- pmdp_idte_global(mm, addr, pmdp);
- atomic_dec(&mm->context.flush_count);
- return old;
- }
- static inline pmd_t pmdp_flush_lazy(struct mm_struct *mm,
- unsigned long addr, pmd_t *pmdp)
- {
- pmd_t old;
- old = *pmdp;
- if (pmd_val(old) & _SEGMENT_ENTRY_INVALID)
- return old;
- atomic_inc(&mm->context.flush_count);
- if (cpumask_equal(&mm->context.cpu_attach_mask,
- cpumask_of(smp_processor_id()))) {
- set_pmd(pmdp, set_pmd_bit(*pmdp, __pgprot(_SEGMENT_ENTRY_INVALID)));
- mm->context.flush_mm = 1;
- } else {
- pmdp_idte_global(mm, addr, pmdp);
- }
- atomic_dec(&mm->context.flush_count);
- return old;
- }
- pmd_t pmdp_xchg_direct(struct mm_struct *mm, unsigned long addr,
- pmd_t *pmdp, pmd_t new)
- {
- pmd_t old;
- preempt_disable();
- old = pmdp_flush_direct(mm, addr, pmdp);
- set_pmd(pmdp, new);
- preempt_enable();
- return old;
- }
- EXPORT_SYMBOL(pmdp_xchg_direct);
- pmd_t pmdp_xchg_lazy(struct mm_struct *mm, unsigned long addr,
- pmd_t *pmdp, pmd_t new)
- {
- pmd_t old;
- preempt_disable();
- old = pmdp_flush_lazy(mm, addr, pmdp);
- set_pmd(pmdp, new);
- preempt_enable();
- return old;
- }
- EXPORT_SYMBOL(pmdp_xchg_lazy);
- static inline void pudp_idte_local(struct mm_struct *mm,
- unsigned long addr, pud_t *pudp)
- {
- if (machine_has_tlb_guest())
- __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
- mm->context.asce, IDTE_LOCAL);
- else
- __pudp_idte(addr, pudp, 0, 0, IDTE_LOCAL);
- }
- static inline void pudp_idte_global(struct mm_struct *mm,
- unsigned long addr, pud_t *pudp)
- {
- if (machine_has_tlb_guest())
- __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
- mm->context.asce, IDTE_GLOBAL);
- else
- __pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL);
- }
- static inline pud_t pudp_flush_direct(struct mm_struct *mm,
- unsigned long addr, pud_t *pudp)
- {
- pud_t old;
- old = *pudp;
- if (pud_val(old) & _REGION_ENTRY_INVALID)
- return old;
- atomic_inc(&mm->context.flush_count);
- if (cpu_has_tlb_lc() &&
- cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
- pudp_idte_local(mm, addr, pudp);
- else
- pudp_idte_global(mm, addr, pudp);
- atomic_dec(&mm->context.flush_count);
- return old;
- }
- pud_t pudp_xchg_direct(struct mm_struct *mm, unsigned long addr,
- pud_t *pudp, pud_t new)
- {
- pud_t old;
- preempt_disable();
- old = pudp_flush_direct(mm, addr, pudp);
- set_pud(pudp, new);
- preempt_enable();
- return old;
- }
- EXPORT_SYMBOL(pudp_xchg_direct);
- #ifdef CONFIG_TRANSPARENT_HUGEPAGE
- void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
- pgtable_t pgtable)
- {
- struct list_head *lh = (struct list_head *) pgtable;
- assert_spin_locked(pmd_lockptr(mm, pmdp));
- /* FIFO */
- if (!pmd_huge_pte(mm, pmdp))
- INIT_LIST_HEAD(lh);
- else
- list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp));
- pmd_huge_pte(mm, pmdp) = pgtable;
- }
- pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
- {
- struct list_head *lh;
- pgtable_t pgtable;
- pte_t *ptep;
- assert_spin_locked(pmd_lockptr(mm, pmdp));
- /* FIFO */
- pgtable = pmd_huge_pte(mm, pmdp);
- lh = (struct list_head *) pgtable;
- if (list_empty(lh))
- pmd_huge_pte(mm, pmdp) = NULL;
- else {
- pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next;
- list_del(lh);
- }
- ptep = (pte_t *) pgtable;
- set_pte(ptep, __pte(_PAGE_INVALID));
- ptep++;
- set_pte(ptep, __pte(_PAGE_INVALID));
- return pgtable;
- }
- #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|