[PATCH] fix race in TSC test

Jan Kiszka <[email protected]> Mon, 18 Feb 2008 17:21:49 +0100
Newsgroups gmane.linux.kernel.tracing
Message-ID <[email protected]>
There is a nasty (but rare, unless you use QEMU) race in
test_tsc_synchronization: If the slave CPU is stuck on the final loop in
test_sync and then gets distracted for a while (NMI watchdog or -that's
how I caught this- QEMU's SMP scheduler), the master could happen to set
wait_end_sync and, shortly after that, reset it again in the next main
loop. We thus end up with a deadlock.

Patch below fixes this by synchronizing the master on the slave to have
left test_sync.

Signed-off-by: Jan Kiszka <[email protected]>

---
 ltt/ltt-test-tsc.c |   14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

Index: b/ltt/ltt-test-tsc.c
===================================================================
--- a/ltt/ltt-test-tsc.c
+++ b/ltt/ltt-test-tsc.c
@@ -59,7 +59,8 @@ static void test_sync(void *arg)
 	per_cpu(wait_end_sync, smp_processor_id())--;
 	do {
 		smp_mb();
-	} while (unlikely(per_cpu(wait_end_sync, wait_cpu) > 0));
+	} while (unlikely(per_cpu(wait_end_sync, wait_cpu) > 1));
+	per_cpu(wait_end_sync, smp_processor_id())--;
 	local_irq_restore(flags);
 }
 
@@ -88,11 +89,18 @@ static int test_tsc_synchronization(void
 			 */
 			per_cpu(wait_sync, master) = 2;
 			per_cpu(wait_sync, cpu) = 2;
-			per_cpu(wait_end_sync, master) = 1;
-			per_cpu(wait_end_sync, cpu) = 1;
+			per_cpu(wait_end_sync, master) = 2;
+			per_cpu(wait_end_sync, cpu) = 2;
 			smp_call_function_single(cpu, test_sync,
 						(void *)master, 1, 0);
 			test_sync((void *)cpu);
+			/*
+			 * Wait until slave is done so that we don't overwrite
+			 * wait_end_sync prematurely.
+			 */
+			while (unlikely(per_cpu(wait_end_sync, cpu) > 0))
+				cpu_relax();
+
 			diff = abs(per_cpu(tsc_count, cpu)
 				- per_cpu(tsc_count, master));
 			best_loop = min(best_loop, diff);