Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/icswx: Fix race condition with IPI setting ACOP

There is a race where a thread causes a coprocessor type to be valid
in its own ACOP _and_ in the current context, but it does not
propagate to the ACOP register of other threads in time for them to
use it. The original code tries to solve this by sending an IPI to
all threads on the system, which is heavy handed, but unfortunately
still provides a window where the icswx is issued by other threads and
the ACOP is not up to date.

This patch detects that the ACOP DSI fault was a "false positive" and
syncs the ACOP and causes the icswx to be replayed.

Signed-off-by: Jimi Xenidis <jimix@pobox.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Jimi Xenidis and committed by
Benjamin Herrenschmidt
de801de1 a6cf7ed5

+27 -2
+21 -2
arch/powerpc/mm/icswx.c
··· 163 163 164 164 static int acop_use_cop(int ct) 165 165 { 166 - /* todo */ 166 + /* There is no alternate policy, yet */ 167 167 return -1; 168 168 } 169 169 ··· 227 227 ct = (ccw >> 16) & 0x3f; 228 228 } 229 229 230 + /* 231 + * We could be here because another thread has enabled acop 232 + * but the ACOP register has yet to be updated. 233 + * 234 + * This should have been taken care of by the IPI to sync all 235 + * the threads (see smp_call_function(sync_cop, mm, 1)), but 236 + * that could take forever if there are a significant amount 237 + * of threads. 238 + * 239 + * Given the number of threads on some of these systems, 240 + * perhaps this is the best way to sync ACOP rather than whack 241 + * every thread with an IPI. 242 + */ 243 + if ((acop_copro_type_bit(ct) & current->active_mm->context.acop) != 0) { 244 + sync_cop(current->active_mm); 245 + return 0; 246 + } 247 + 248 + /* check for alternate policy */ 230 249 if (!acop_use_cop(ct)) 231 250 return 0; 232 251 233 252 /* at this point the CT is unknown to the system */ 234 - pr_warn("%s[%d]: Coprocessor %d is unavailable", 253 + pr_warn("%s[%d]: Coprocessor %d is unavailable\n", 235 254 current->comm, current->pid, ct); 236 255 237 256 /* get inst if we don't already have it */
+6
arch/powerpc/mm/icswx.h
··· 59 59 60 60 extern int acop_handle_fault(struct pt_regs *regs, unsigned long address, 61 61 unsigned long error_code); 62 + 63 + static inline u64 acop_copro_type_bit(unsigned int type) 64 + { 65 + return 1ULL << (63 - type); 66 + } 67 + 62 68 #endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */