commit: 57cdfdf829a850a317425ed93c6a576c9ee6329c
From: Anton Blanchard <[email protected]>
Date: Thu, 21 Oct 2010 00:52:12 +0000
Subject: [PATCH] powerpc: Fix hcall tracepoint recursion

Spinlocks on shared processor partitions use H_YIELD to notify the
hypervisor we are waiting on another virtual CPU. Unfortunately this means
the hcall tracepoints can recurse.

The patch below adds a percpu depth and checks it on both the entry and
exit hcall tracepoints.

Signed-off-by: Anton Blanchard <[email protected]>
Acked-by: Steven Rostedt <[email protected]>
Signed-off-by: Benjamin Herrenschmidt <[email protected]>
CC: [email protected]
---
 arch/powerpc/platforms/pseries/lpar.c |   37 +++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/lpar.c 
b/arch/powerpc/platforms/pseries/lpar.c
index 5d3ea9f..ca5d589 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -713,6 +713,13 @@ EXPORT_SYMBOL(arch_free_page);
 /* NB: reg/unreg are called while guarded with the tracepoints_mutex */
 extern long hcall_tracepoint_refcount;
 
+/* 
+ * Since the tracing code might execute hcalls we need to guard against
+ * recursion. One example of this are spinlocks calling H_YIELD on
+ * shared processor partitions.
+ */
+static DEFINE_PER_CPU(unsigned int, hcall_trace_depth);
+
 void hcall_tracepoint_regfunc(void)
 {
        hcall_tracepoint_refcount++;
@@ -725,12 +732,42 @@ void hcall_tracepoint_unregfunc(void)
 
 void __trace_hcall_entry(unsigned long opcode, unsigned long *args)
 {
+       unsigned long flags;
+       unsigned int *depth;
+
+       local_irq_save(flags);
+
+       depth = &__get_cpu_var(hcall_trace_depth);
+
+       if (*depth)
+               goto out;
+
+       (*depth)++;
        trace_hcall_entry(opcode, args);
+       (*depth)--;
+
+out:
+       local_irq_restore(flags);
 }
 
 void __trace_hcall_exit(long opcode, unsigned long retval,
                        unsigned long *retbuf)
 {
+       unsigned long flags;
+       unsigned int *depth;
+
+       local_irq_save(flags);
+
+       depth = &__get_cpu_var(hcall_trace_depth);
+
+       if (*depth)
+               goto out;
+
+       (*depth)++;
        trace_hcall_exit(opcode, retval, retbuf);
+       (*depth)--;
+
+out:
+       local_irq_restore(flags);
 }
 #endif

_______________________________________________
stable mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to