Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 May 2010 21:07:58 +0000 (UTC)
From:      Nathan Whitehorn <nwhitehorn@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r208364 - head/sys/powerpc/aim
Message-ID:  <201005202107.o4KL7wtH009945@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: nwhitehorn
Date: Thu May 20 21:07:58 2010
New Revision: 208364
URL: http://svn.freebsd.org/changeset/base/208364

Log:
  Fix a long-standing bug in the PowerPC OFW call function on SMP machines
  where running ofwdump could cause hangs by forcing all secondary CPUs
  into a busy wait with interrupts off during the call.
  
  Following section 8.4 of the Open Firmware PowerPC processor binding,
  the firmware is free to overwrite the system interrupt handlers during
  OF calls, restoring the OS handlers on exit. On single CPU systems, this
  process is invisible to the operating system. On multiple CPU systems,
  taking any exception on a secondary CPU while an OF call is in progress
  ends with that exception vectored into OF, resulting in a slow movement
  of the entire system into firmware context and a machine hang.
  
  MFC after:	3 days

Modified:
  head/sys/powerpc/aim/ofw_machdep.c

Modified: head/sys/powerpc/aim/ofw_machdep.c
==============================================================================
--- head/sys/powerpc/aim/ofw_machdep.c	Thu May 20 20:15:56 2010	(r208363)
+++ head/sys/powerpc/aim/ofw_machdep.c	Thu May 20 21:07:58 2010	(r208364)
@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/disk.h>
 #include <sys/fcntl.h>
 #include <sys/malloc.h>
+#include <sys/smp.h>
 #include <sys/stat.h>
 
 #include <net/ethernet.h>
@@ -348,17 +349,18 @@ ofw_quiesce(void)
 }
 
 static int
-openfirmware(void *args)
+openfirmware_core(void *args)
 {
 	long	oldmsr;
 	int	result;
 	u_int	srsave[16];
 	u_int   i;
 
-	if (pmap_bootstrapped && ofw_real_mode)
-		args = (void *)pmap_kextract((vm_offset_t)args);
-
-	mtx_lock(&ofw_mutex);
+	/*
+	 * NOTE: This MUST be called with the OF mutex held. Because the CPU
+	 * holding the lock is not necessarily the CPU running this function,
+	 * we can't put an assert here.
+	 */
 
 	__asm __volatile(	"\t"
 		"sync\n\t"
@@ -412,6 +414,63 @@ openfirmware(void *args)
 		: : "r" (oldmsr)
 	);
 
+	return (result);
+}
+
+#ifdef SMP
+struct ofw_rv_args {
+	void *args;
+	int retval;
+	volatile int in_progress;
+};
+
+static void
+ofw_rendezvous_dispatch(void *xargs)
+{
+	struct ofw_rv_args *rv_args = xargs;
+
+	/* NOTE: Interrupts are disabled here */
+
+	if (PCPU_GET(cpuid) == 0) {
+		/*
+		 * Execute all OF calls on CPU 0
+		 */
+		rv_args->retval = openfirmware_core(rv_args->args);
+		rv_args->in_progress = 0;
+	} else {
+		/*
+		 * Spin with interrupts off on other CPUs while OF has
+		 * control of the machine.
+		 */
+		while (rv_args->in_progress)
+			cpu_spinwait();
+	}
+}
+#endif
+
+static int
+openfirmware(void *args)
+{
+	int result;
+	#ifdef SMP
+	struct ofw_rv_args rv_args;
+	#endif
+
+	if (pmap_bootstrapped && ofw_real_mode)
+		args = (void *)pmap_kextract((vm_offset_t)args);
+
+	mtx_lock(&ofw_mutex);
+
+	#ifdef SMP
+	rv_args.args = args;
+	rv_args.in_progress = 1;
+	smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,
+	    smp_no_rendevous_barrier, &rv_args);
+	result = rv_args.retval;
+	#else
+	result = openfirmware_core(args);
+	#endif
+
 	mtx_unlock(&ofw_mutex);
 
 	return (result);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201005202107.o4KL7wtH009945>