aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimi Xenidis <jimix@pobox.com>2012-02-28 08:27:07 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-03-07 01:06:09 -0500
commitde801de139ba2a2f2c74393ea00a321477ecc0dc (patch)
treef1850331426e3c1d4b0e7a20e633095f2546bf8f
parenta6cf7ed5119fb22f54584a9f867b638edd3c4384 (diff)
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>
-rw-r--r--arch/powerpc/mm/icswx.c23
-rw-r--r--arch/powerpc/mm/icswx.h6
2 files changed, 27 insertions, 2 deletions
diff --git a/arch/powerpc/mm/icswx.c b/arch/powerpc/mm/icswx.c
index 5d9a59eaad93..8cdbd8634a58 100644
--- a/arch/powerpc/mm/icswx.c
+++ b/arch/powerpc/mm/icswx.c
@@ -163,7 +163,7 @@ EXPORT_SYMBOL_GPL(drop_cop);
163 163
164static int acop_use_cop(int ct) 164static int acop_use_cop(int ct)
165{ 165{
166 /* todo */ 166 /* There is no alternate policy, yet */
167 return -1; 167 return -1;
168} 168}
169 169
@@ -227,11 +227,30 @@ int acop_handle_fault(struct pt_regs *regs, unsigned long address,
227 ct = (ccw >> 16) & 0x3f; 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 if (!acop_use_cop(ct)) 249 if (!acop_use_cop(ct))
231 return 0; 250 return 0;
232 251
233 /* at this point the CT is unknown to the system */ 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 current->comm, current->pid, ct); 254 current->comm, current->pid, ct);
236 255
237 /* get inst if we don't already have it */ 256 /* get inst if we don't already have it */
diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h
index 42176bd0884c..6dedc08e62c8 100644
--- a/arch/powerpc/mm/icswx.h
+++ b/arch/powerpc/mm/icswx.h
@@ -59,4 +59,10 @@ extern void free_cop_pid(int free_pid);
59 59
60extern int acop_handle_fault(struct pt_regs *regs, unsigned long address, 60extern int acop_handle_fault(struct pt_regs *regs, unsigned long address,
61 unsigned long error_code); 61 unsigned long error_code);
62
63static inline u64 acop_copro_type_bit(unsigned int type)
64{
65 return 1ULL << (63 - type);
66}
67
62#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */ 68#endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */