svn commit: r208364 - head/sys/powerpc/aim
Nathan Whitehorn
nwhitehorn at FreeBSD.org
Thu May 20 21:07:58 UTC 2010
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);
More information about the svn-src-all
mailing list