Commit cca19f0b authored by Frederic Barrat's avatar Frederic Barrat Committed by Michael Ellerman

powerpc/64s/radix: Fix missing global invalidations when removing copro

With the optimizations for TLB invalidation from commit 0cef77c7
("powerpc/64s/radix: flush remote CPUs out of single-threaded
mm_cpumask"), the scope of a TLBI (global vs. local) can now be
influenced by the value of the 'copros' counter of the memory context.

When calling mm_context_remove_copro(), the 'copros' counter is
decremented first before flushing. It may have the unintended side
effect of sending local TLBIs when we explicitly need global
invalidations in this case. Thus breaking any nMMU user in a bad and
unpredictable way.

Fix it by flushing first, before updating the 'copros' counter, so
that invalidations will be global.

Fixes: 0cef77c7 ("powerpc/64s/radix: flush remote CPUs out of single-threaded mm_cpumask")
Signed-off-by: default avatarFrederic Barrat <fbarrat@linux.ibm.com>
Reviewed-by: default avatarNicholas Piggin <npiggin@gmail.com>
Tested-by: default avatarVaibhav Jain <vaibhav@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent b03897cf
...@@ -143,24 +143,33 @@ static inline void mm_context_remove_copro(struct mm_struct *mm) ...@@ -143,24 +143,33 @@ static inline void mm_context_remove_copro(struct mm_struct *mm)
{ {
int c; int c;
c = atomic_dec_if_positive(&mm->context.copros);
/* Detect imbalance between add and remove */
WARN_ON(c < 0);
/* /*
* Need to broadcast a global flush of the full mm before * When removing the last copro, we need to broadcast a global
* decrementing active_cpus count, as the next TLBI may be * flush of the full mm, as the next TLBI may be local and the
* local and the nMMU and/or PSL need to be cleaned up. * nMMU and/or PSL need to be cleaned up.
* Should be rare enough so that it's acceptable. *
* Both the 'copros' and 'active_cpus' counts are looked at in
* flush_all_mm() to determine the scope (local/global) of the
* TLBIs, so we need to flush first before decrementing
* 'copros'. If this API is used by several callers for the
* same context, it can lead to over-flushing. It's hopefully
* not common enough to be a problem.
* *
* Skip on hash, as we don't know how to do the proper flush * Skip on hash, as we don't know how to do the proper flush
* for the time being. Invalidations will remain global if * for the time being. Invalidations will remain global if
* used on hash. * used on hash. Note that we can't drop 'copros' either, as
* it could make some invalidations local with no flush
* in-between.
*/ */
if (c == 0 && radix_enabled()) { if (radix_enabled()) {
flush_all_mm(mm); flush_all_mm(mm);
dec_mm_active_cpus(mm);
c = atomic_dec_if_positive(&mm->context.copros);
/* Detect imbalance between add and remove */
WARN_ON(c < 0);
if (c == 0)
dec_mm_active_cpus(mm);
} }
} }
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment