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>