>From 16c0d101ef2be7916e0088eebb81013702d0d0d8 Mon Sep 17 00:00:00 2001 From: yhb <yhb@xxxxxxxxxxxxx> Date: Sun, 10 Jul 2016 19:39:43 +0800 Subject: [PATCH] MIPS: Make the ASID wrapping problem expose earlier Our company uses the Linux kernel 2.6.32.13, and recently the ASID wrapping problem for the MIPS64 CPUs gets us into trouble: Many processes crash every about 100 days. We find out that CPU ASID wraps to 0. struct cpuinfo_mips { unsigned int udelay_val; unsigned int asid_cache;/* The type of asid_cache is unsigned int! */ ... }; static inline void get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) { unsigned long asid = asid_cache(cpu);/* (1)The type of asid_cache(cpu) is unsigned int. Suppose its current value is 0xFFFF FFFF. */ if (! ((asid += ASID_INC) & ASID_MASK) ) {/* (2)asid is 0x1 0000 0000. */ ... if (!asid) /* (3)asid is not zero! */ asid = ASID_FIRST_VERSION; } cpu_context(cpu, mm) = asid_cache(cpu) = asid;/* (4)Both cpu_context(cpu, mm) and asid_cache(cpu) are zero! */ } Function switch_mm: /* Check if our ASID is of an older version and thus invalid */ if ((cpu_context(cpu, next) ^ asid_cache(cpu)) & ASID_VERSION_MASK) get_new_mmu_context(next, cpu); If the version of asid_cache(cpu) is zero, and cpu_context(cpu, next) is zero, this function will not assign new ASID to the next process. It is possible that the ASIDs of many processes are zero, and many processes crash. Many Linux distributions such as 2.6.32.71 and 3.10.x don't fix this bug. I strongly advise that the community apply the patch "MIPS: Change type of asid_cache to unsigned long" to all longterm branches. In order to make the ASID wrapping problem expose earlier, set the initial value of asid_cache(cpu) to ASID_INITIAL_VALUE(((unsigned long)-1) & ASID_VERSION_MASK). In order to prevent later revision from introducing the bug again, compare the length of asid and asid_cache(cpu) in function get_new_mmu_context and kvm_get_new_mmu_context: BUILD_BUG_ON(sizeof(asid) != sizeof(asid_cache(cpu))); Signed-off-by: yhb <yhb@xxxxxxxxxxxxx> --- arch/mips/include/asm/mmu_context.h | 12 +++++++++++- arch/mips/kernel/traps.c | 2 +- arch/mips/kvm/tlb.c | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h index 45914b5..9e492ff 100644 --- a/arch/mips/include/asm/mmu_context.h +++ b/arch/mips/include/asm/mmu_context.h @@ -97,6 +97,13 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) #define ASID_VERSION_MASK ((unsigned long)~(ASID_MASK|(ASID_MASK-1))) #define ASID_FIRST_VERSION ((unsigned long)(~ASID_VERSION_MASK) + 1) +/* + * Yu Huabing + * In order to make the ASID wrapping problem expose earlier, + * set the initial value of asid_cache(cpu) to a large value. + */ +#define ASID_INITIAL_VALUE (((unsigned long)-1) & ASID_VERSION_MASK) + /* Normal, classic MIPS get_new_mmu_context */ static inline void get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) @@ -104,7 +111,10 @@ get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) extern void kvm_local_flush_tlb_all(void); unsigned long asid = asid_cache(cpu); - if (! ((asid += ASID_INC) & ASID_MASK) ) { + BUILD_BUG_ON(sizeof(asid) != sizeof(asid_cache(cpu))); + + asid += ASID_INC; + if (!(asid & ASID_MASK)) { if (cpu_has_vtag_icache) flush_icache_all(); #ifdef CONFIG_KVM diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 4d601737..6264cd8 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -2139,7 +2139,7 @@ void per_cpu_trap_init(bool is_boot_cpu) } if (!cpu_data[cpu].asid_cache) - cpu_data[cpu].asid_cache = ASID_FIRST_VERSION; + cpu_data[cpu].asid_cache = ASID_INITIAL_VALUE; atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; diff --git a/arch/mips/kvm/tlb.c b/arch/mips/kvm/tlb.c index e0e1d0a..73c8d7c 100644 --- a/arch/mips/kvm/tlb.c +++ b/arch/mips/kvm/tlb.c @@ -564,6 +564,8 @@ void kvm_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu, { unsigned long asid = asid_cache(cpu); + BUILD_BUG_ON(sizeof(asid) != sizeof(asid_cache(cpu))); + asid += ASID_INC; if (!(asid & ASID_MASK)) { if (cpu_has_vtag_icache) --