Blob Blame History Raw
From: Martin Schwidefsky <schwidefsky@de.ibm.com>
Date: Tue, 8 Jan 2019 12:44:57 +0100
Subject: s390/mm: always force a load of the primary ASCE on context switch
Git-commit: a38662084c8bdb829ff486468c7ea801c13fcc34
Patch-mainline: v5.0 or v5.0-rc3 (next release)
References: git-fixes

The ASCE of an mm_struct can be modified after a task has been created,
e.g. via crst_table_downgrade for a compat process. The active_mm logic
to avoid the switch_mm call if the next task is a kernel thread can
lead to a situation where switch_mm is called where 'prev == next' is
true but 'prev->context.asce == next->context.asce' is not.

This can lead to a situation where a CPU uses the outdated ASCE to run
a task. The result can be a crash, endless loops and really subtle
problem due to TLBs being created with an invalid ASCE.

Cc: stable@kernel.org # v3.15+
Fixes: 53e857f30867 ("s390/mm,tlb: race of lazy TLB flush vs. recreation")
Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
[ ptesarik: Adjusted context, because SLE15 does not contain upstream
  commit a38662084c8bdb829ff486468c7ea801c13fcc34. ]
Signed-off-by: Petr Tesarik <ptesarik@suse.com>
---
 arch/s390/include/asm/mmu_context.h |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -96,13 +96,12 @@ static inline void switch_mm(struct mm_s
 	int cpu = smp_processor_id();
 
 	S390_lowcore.user_asce = next->context.asce;
-	if (prev == next)
-		return;
 	cpumask_set_cpu(cpu, &next->context.cpu_attach_mask);
 	/* Clear old ASCE by loading the kernel ASCE. */
 	__ctl_load(S390_lowcore.kernel_asce, 1, 1);
 	__ctl_load(S390_lowcore.kernel_asce, 7, 7);
-	cpumask_clear_cpu(cpu, &prev->context.cpu_attach_mask);
+	if (prev != next)
+		cpumask_clear_cpu(cpu, &prev->context.cpu_attach_mask);
 }
 
 #define finish_arch_post_lock_switch finish_arch_post_lock_switch