8a4e3a9ead
mm->context.id is updated under asid_lock when a new ASID is allocated to an mm_struct. However, it is also read without the lock when a task is being scheduled and checking whether or not the current ASID generation is up-to-date. If two threads of the same process are being scheduled in parallel and the bottom bits of the generation in their mm->context.id match the current generation (that is, the mm_struct has not been used for ~2^24 rollovers) then the non-atomic, lockless access to mm->context.id may yield the incorrect ASID. This patch fixes this issue by making mm->context.id and atomic64_t, ensuring that the generation is always read consistently. For code that only requires access to the ASID bits (e.g. TLB flushing by mm), then the value is accessed directly, which GCC converts to an ldrb. Cc: <stable@vger.kernel.org> # 3.8 Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
116 lines
3.0 KiB
C
116 lines
3.0 KiB
C
/*
|
|
* arch/arm/include/asm/mmu_context.h
|
|
*
|
|
* Copyright (C) 1996 Russell King.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Changelog:
|
|
* 27-06-1996 RMK Created
|
|
*/
|
|
#ifndef __ASM_ARM_MMU_CONTEXT_H
|
|
#define __ASM_ARM_MMU_CONTEXT_H
|
|
|
|
#include <linux/compiler.h>
|
|
#include <linux/sched.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cachetype.h>
|
|
#include <asm/proc-fns.h>
|
|
#include <asm-generic/mm_hooks.h>
|
|
|
|
void __check_vmalloc_seq(struct mm_struct *mm);
|
|
|
|
#ifdef CONFIG_CPU_HAS_ASID
|
|
|
|
void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk);
|
|
#define init_new_context(tsk,mm) ({ atomic64_set(&mm->context.id, 0); 0; })
|
|
|
|
#else /* !CONFIG_CPU_HAS_ASID */
|
|
|
|
#ifdef CONFIG_MMU
|
|
|
|
static inline void check_and_switch_context(struct mm_struct *mm,
|
|
struct task_struct *tsk)
|
|
{
|
|
if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq))
|
|
__check_vmalloc_seq(mm);
|
|
|
|
if (irqs_disabled())
|
|
/*
|
|
* cpu_switch_mm() needs to flush the VIVT caches. To avoid
|
|
* high interrupt latencies, defer the call and continue
|
|
* running with the old mm. Since we only support UP systems
|
|
* on non-ASID CPUs, the old mm will remain valid until the
|
|
* finish_arch_post_lock_switch() call.
|
|
*/
|
|
set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM);
|
|
else
|
|
cpu_switch_mm(mm->pgd, mm);
|
|
}
|
|
|
|
#define finish_arch_post_lock_switch \
|
|
finish_arch_post_lock_switch
|
|
static inline void finish_arch_post_lock_switch(void)
|
|
{
|
|
if (test_and_clear_thread_flag(TIF_SWITCH_MM)) {
|
|
struct mm_struct *mm = current->mm;
|
|
cpu_switch_mm(mm->pgd, mm);
|
|
}
|
|
}
|
|
|
|
#endif /* CONFIG_MMU */
|
|
|
|
#define init_new_context(tsk,mm) 0
|
|
|
|
#endif /* CONFIG_CPU_HAS_ASID */
|
|
|
|
#define destroy_context(mm) do { } while(0)
|
|
#define activate_mm(prev,next) switch_mm(prev, next, NULL)
|
|
|
|
/*
|
|
* This is called when "tsk" is about to enter lazy TLB mode.
|
|
*
|
|
* mm: describes the currently active mm context
|
|
* tsk: task which is entering lazy tlb
|
|
* cpu: cpu number which is entering lazy tlb
|
|
*
|
|
* tsk->mm will be NULL
|
|
*/
|
|
static inline void
|
|
enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* This is the actual mm switch as far as the scheduler
|
|
* is concerned. No registers are touched. We avoid
|
|
* calling the CPU specific function when the mm hasn't
|
|
* actually changed.
|
|
*/
|
|
static inline void
|
|
switch_mm(struct mm_struct *prev, struct mm_struct *next,
|
|
struct task_struct *tsk)
|
|
{
|
|
#ifdef CONFIG_MMU
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* check for possible thread migration */
|
|
if (!cpumask_empty(mm_cpumask(next)) &&
|
|
!cpumask_test_cpu(cpu, mm_cpumask(next)))
|
|
__flush_icache_all();
|
|
#endif
|
|
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) {
|
|
check_and_switch_context(next, tsk);
|
|
if (cache_is_vivt())
|
|
cpumask_clear_cpu(cpu, mm_cpumask(prev));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#define deactivate_mm(tsk,mm) do { } while (0)
|
|
|
|
#endif
|