summaryrefslogtreecommitdiff
path: root/arch/s390/include
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/include')
-rw-r--r--arch/s390/include/asm/mmu_context.h39
-rw-r--r--arch/s390/include/asm/pgtable.h60
-rw-r--r--arch/s390/include/asm/thread_info.h2
3 files changed, 73 insertions, 28 deletions
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index 5d1f950704dc..38149b63dc44 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -48,13 +48,42 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
- cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
- update_mm(next, tsk);
+ int cpu = smp_processor_id();
+
+ if (prev == next)
+ return;
+ if (atomic_inc_return(&next->context.attach_count) >> 16) {
+ /* Delay update_mm until all TLB flushes are done. */
+ set_tsk_thread_flag(tsk, TIF_TLB_WAIT);
+ } else {
+ cpumask_set_cpu(cpu, mm_cpumask(next));
+ update_mm(next, tsk);
+ if (next->context.flush_mm)
+ /* Flush pending TLBs */
+ __tlb_flush_mm(next);
+ }
atomic_dec(&prev->context.attach_count);
WARN_ON(atomic_read(&prev->context.attach_count) < 0);
- atomic_inc(&next->context.attach_count);
- /* Check for TLBs not flushed yet */
- __tlb_flush_mm_lazy(next);
+}
+
+#define finish_arch_post_lock_switch finish_arch_post_lock_switch
+static inline void finish_arch_post_lock_switch(void)
+{
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+
+ if (!test_tsk_thread_flag(tsk, TIF_TLB_WAIT))
+ return;
+ preempt_disable();
+ clear_tsk_thread_flag(tsk, TIF_TLB_WAIT);
+ while (atomic_read(&mm->context.attach_count) >> 16)
+ cpu_relax();
+
+ cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+ update_mm(mm, tsk);
+ if (mm->context.flush_mm)
+ __tlb_flush_mm(mm);
+ preempt_enable();
}
#define enter_lazy_tlb(mm,tsk) do { } while (0)
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 2204400d0bd5..fc4bb82a0739 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -1034,30 +1034,41 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm,
static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
{
- if (!(pte_val(*ptep) & _PAGE_INVALID)) {
+ unsigned long pto = (unsigned long) ptep;
+
#ifndef CONFIG_64BIT
- /* pto must point to the start of the segment table */
- pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
-#else
- /* ipte in zarch mode can do the math */
- pte_t *pto = ptep;
+ /* pto in ESA mode must point to the start of the segment table */
+ pto &= 0x7ffffc00;
#endif
- asm volatile(
- " ipte %2,%3"
- : "=m" (*ptep) : "m" (*ptep),
- "a" (pto), "a" (address));
- }
+ /* Invalidation + global TLB flush for the pte */
+ asm volatile(
+ " ipte %2,%3"
+ : "=m" (*ptep) : "m" (*ptep), "a" (pto), "a" (address));
+}
+
+static inline void ptep_flush_direct(struct mm_struct *mm,
+ unsigned long address, pte_t *ptep)
+{
+ if (pte_val(*ptep) & _PAGE_INVALID)
+ return;
+ __ptep_ipte(address, ptep);
}
static inline void ptep_flush_lazy(struct mm_struct *mm,
unsigned long address, pte_t *ptep)
{
- int active = (mm == current->active_mm) ? 1 : 0;
+ int active, count;
- if (atomic_read(&mm->context.attach_count) > active)
- __ptep_ipte(address, ptep);
- else
+ if (pte_val(*ptep) & _PAGE_INVALID)
+ return;
+ active = (mm == current->active_mm) ? 1 : 0;
+ count = atomic_add_return(0x10000, &mm->context.attach_count);
+ if ((count & 0xffff) <= active) {
+ pte_val(*ptep) |= _PAGE_INVALID;
mm->context.flush_mm = 1;
+ } else
+ __ptep_ipte(address, ptep);
+ atomic_sub(0x10000, &mm->context.attach_count);
}
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
@@ -1074,7 +1085,7 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
}
pte = *ptep;
- __ptep_ipte(addr, ptep);
+ ptep_flush_direct(vma->vm_mm, addr, ptep);
young = pte_young(pte);
pte = pte_mkold(pte);
@@ -1145,7 +1156,6 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm,
pte = *ptep;
ptep_flush_lazy(mm, address, ptep);
- pte_val(*ptep) |= _PAGE_INVALID;
if (mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
@@ -1182,7 +1192,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
}
pte = *ptep;
- __ptep_ipte(address, ptep);
+ ptep_flush_direct(vma->vm_mm, address, ptep);
pte_val(*ptep) = _PAGE_INVALID;
if (mm_has_pgste(vma->vm_mm)) {
@@ -1263,7 +1273,7 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste);
}
- __ptep_ipte(address, ptep);
+ ptep_flush_direct(vma->vm_mm, address, ptep);
if (mm_has_pgste(vma->vm_mm)) {
pgste_set_pte(ptep, entry);
@@ -1447,12 +1457,16 @@ static inline pmd_t pmd_mkwrite(pmd_t pmd)
static inline void pmdp_flush_lazy(struct mm_struct *mm,
unsigned long address, pmd_t *pmdp)
{
- int active = (mm == current->active_mm) ? 1 : 0;
+ int active, count;
- if ((atomic_read(&mm->context.attach_count) & 0xffff) > active)
- __pmd_idte(address, pmdp);
- else
+ active = (mm == current->active_mm) ? 1 : 0;
+ count = atomic_add_return(0x10000, &mm->context.attach_count);
+ if ((count & 0xffff) <= active) {
+ pmd_val(*pmdp) |= _SEGMENT_ENTRY_INVALID;
mm->context.flush_mm = 1;
+ } else
+ __pmd_idte(address, pmdp);
+ atomic_sub(0x10000, &mm->context.attach_count);
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h
index 10e0fcd3633d..d2e53d667a92 100644
--- a/arch/s390/include/asm/thread_info.h
+++ b/arch/s390/include/asm/thread_info.h
@@ -81,6 +81,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_NOTIFY_RESUME 1 /* callback before returning to user */
#define TIF_SIGPENDING 2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
+#define TIF_TLB_WAIT 4 /* wait for TLB flush completion */
#define TIF_PER_TRAP 6 /* deliver sigtrap on return to user */
#define TIF_MCCK_PENDING 7 /* machine check handling is pending */
#define TIF_SYSCALL_TRACE 8 /* syscall trace active */
@@ -96,6 +97,7 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_SIGPENDING (1<<TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
+#define _TIF_TLB_WAIT (1<<TIF_TLB_WAIT)
#define _TIF_PER_TRAP (1<<TIF_PER_TRAP)
#define _TIF_MCCK_PENDING (1<<TIF_MCCK_PENDING)
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)