summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2009-08-13 17:09:45 +0000
committerattilio <attilio@FreeBSD.org>2009-08-13 17:09:45 +0000
commite85ca71aadcc42a3f7cc90e6cff96c28f1c54290 (patch)
treefa01b2cb8792e972d05922a5507e082efa4d4c1d /sys/i386
parentb1786d80d2f6e85141431d158b4cf3a47a2f71ec (diff)
downloadFreeBSD-src-e85ca71aadcc42a3f7cc90e6cff96c28f1c54290.zip
FreeBSD-src-e85ca71aadcc42a3f7cc90e6cff96c28f1c54290.tar.gz
* Completely Remove the option STOP_NMI from the kernel. This option
has proven to have a good effect when entering KDB by using a NMI, but it completely violates all the good rules about interrupts disabled while holding a spinlock in other occasions. This can be the cause of deadlocks on events where a normal IPI_STOP is expected. * Adds an new IPI called IPI_STOP_HARD on all the supported architectures. This IPI is responsible for sending a stop message among CPUs using a privileged channel when disponible. In other cases it just does match a normal IPI_STOP. Right now the IPI_STOP_HARD functionality uses a NMI on ia32 and amd64 architectures, while on the other has a normal IPI_STOP effect. It is responsibility of maintainers to eventually implement an hard stop when necessary and possible. * Use the new IPI facility in order to implement a new userend SMP kernel function called stop_cpus_hard(). That is specular to stop_cpu() but it does use the privileged channel for the stopping facility. * Let KDB use the newly introduced function stop_cpus_hard() and leave stop_cpus() for all the other cases * Disable interrupts on CPU0 when starting the process of APs suspension. * Style cleanup and comments adding This patch should fix the reboot/shutdown deadlocks many users are constantly reporting on mailing lists. Please don't forget to update your config file with the STOP_NMI option removal Reviewed by: jhb Tested by: pho, bz, rink Approved by: re (kib)
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/conf/GENERIC1
-rw-r--r--sys/i386/conf/NOTES1
-rw-r--r--sys/i386/i386/local_apic.c13
-rw-r--r--sys/i386/i386/mp_machdep.c99
-rw-r--r--sys/i386/i386/trap.c2
-rw-r--r--sys/i386/include/apicvar.h7
-rw-r--r--sys/i386/include/smp.h6
-rw-r--r--sys/i386/xen/mp_machdep.c2
8 files changed, 45 insertions, 86 deletions
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 02f5a36..ef958af 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -70,7 +70,6 @@ options P1003_1B_SEMAPHORES # POSIX-style semaphores
options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
options KBD_INSTALL_CDEV # install a CDEV entry in /dev
-options STOP_NMI # Stop CPUS using NMI instead of IPI
options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4)
options AUDIT # Security event auditing
options MAC # TrustedBSD MAC Framework
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index f442e24..f772b25 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -49,7 +49,6 @@ options MP_WATCHDOG
# Debugging options.
#
-options STOP_NMI # Stop CPUS using NMI instead of IPI
options COUNT_XINVLTLB_HITS # Counters for TLB events
options COUNT_IPIS # Per-CPU IPI interrupt counters
diff --git a/sys/i386/i386/local_apic.c b/sys/i386/i386/local_apic.c
index 6b350e2..2cc6a45 100644
--- a/sys/i386/i386/local_apic.c
+++ b/sys/i386/i386/local_apic.c
@@ -1248,8 +1248,17 @@ lapic_ipi_vectored(u_int vector, int dest)
KASSERT((vector & ~APIC_VECTOR_MASK) == 0,
("%s: invalid vector %d", __func__, vector));
- icrlo = vector | APIC_DELMODE_FIXED | APIC_DESTMODE_PHY |
- APIC_LEVEL_DEASSERT | APIC_TRIGMOD_EDGE;
+ icrlo = APIC_DESTMODE_PHY | APIC_TRIGMOD_EDGE;
+
+ /*
+ * IPI_STOP_HARD is just a "fake" vector used to send a NMI.
+ * Use special rules regard NMI if passed, otherwise specify
+ * the vector.
+ */
+ if (vector == IPI_STOP_HARD)
+ icrlo |= APIC_DELMODE_NMI | APIC_LEVEL_ASSERT;
+ else
+ icrlo |= vector | APIC_DELMODE_FIXED | APIC_LEVEL_DEASSERT;
destfield = 0;
switch (dest) {
case APIC_IPI_DEST_SELF:
diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c
index 0bfe91d..6729288 100644
--- a/sys/i386/i386/mp_machdep.c
+++ b/sys/i386/i386/mp_machdep.c
@@ -155,12 +155,6 @@ vm_offset_t smp_tlb_addr1;
vm_offset_t smp_tlb_addr2;
volatile int smp_tlb_wait;
-#ifdef STOP_NMI
-static volatile cpumask_t ipi_nmi_pending;
-
-static void ipi_nmi_selected(cpumask_t cpus);
-#endif
-
#ifdef COUNT_IPIS
/* Interrupt counts. */
static u_long *ipi_preempt_counts[MAXCPU];
@@ -177,21 +171,8 @@ u_long *ipi_lazypmap_counts[MAXCPU];
* Local data and functions.
*/
-#ifdef STOP_NMI
-/*
- * Provide an alternate method of stopping other CPUs. If another CPU has
- * disabled interrupts the conventional STOP IPI will be blocked. This
- * NMI-based stop should get through in that case.
- */
-static int stop_cpus_with_nmi = 1;
-SYSCTL_INT(_debug, OID_AUTO, stop_cpus_with_nmi, CTLTYPE_INT | CTLFLAG_RW,
- &stop_cpus_with_nmi, 0, "");
-TUNABLE_INT("debug.stop_cpus_with_nmi", &stop_cpus_with_nmi);
-#else
-#define stop_cpus_with_nmi 0
-#endif
-
static u_int logical_cpus;
+static volatile cpumask_t ipi_nmi_pending;
/* used to hold the AP's until we are ready to release them */
static struct mtx ap_boot_mtx;
@@ -1318,12 +1299,14 @@ ipi_selected(cpumask_t cpus, u_int ipi)
ipi = IPI_BITMAP_VECTOR;
}
-#ifdef STOP_NMI
- if (ipi == IPI_STOP && stop_cpus_with_nmi) {
- ipi_nmi_selected(cpus);
- return;
- }
-#endif
+ /*
+ * IPI_STOP_HARD maps to a NMI and the trap handler needs a bit
+ * of help in order to understand what is the source.
+ * Set the mask of receiving CPUs for this purpose.
+ */
+ if (ipi == IPI_STOP_HARD)
+ atomic_set_int(&ipi_nmi_pending, cpus);
+
CTR3(KTR_SMP, "%s: cpus: %x ipi: %x", __func__, cpus, ipi);
while ((cpu = ffs(cpus)) != 0) {
cpu--;
@@ -1354,64 +1337,42 @@ void
ipi_all_but_self(u_int ipi)
{
- if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
+ if (IPI_IS_BITMAPED(ipi)) {
ipi_selected(PCPU_GET(other_cpus), ipi);
return;
}
+
+ /*
+ * IPI_STOP_HARD maps to a NMI and the trap handler needs a bit
+ * of help in order to understand what is the source.
+ * Set the mask of receiving CPUs for this purpose.
+ */
+ if (ipi == IPI_STOP_HARD)
+ atomic_set_int(&ipi_nmi_pending, PCPU_GET(other_cpus));
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS);
}
-#ifdef STOP_NMI
-/*
- * send NMI IPI to selected CPUs
- */
-
-#define BEFORE_SPIN 1000000
-
-void
-ipi_nmi_selected(cpumask_t cpus)
-{
- int cpu;
- register_t icrlo;
-
- icrlo = APIC_DELMODE_NMI | APIC_DESTMODE_PHY | APIC_LEVEL_ASSERT
- | APIC_TRIGMOD_EDGE;
-
- CTR2(KTR_SMP, "%s: cpus: %x nmi", __func__, cpus);
-
- atomic_set_int(&ipi_nmi_pending, cpus);
-
- while ((cpu = ffs(cpus)) != 0) {
- cpu--;
- cpus &= ~(1 << cpu);
-
- KASSERT(cpu_apic_ids[cpu] != -1,
- ("IPI NMI to non-existent CPU %d", cpu));
-
- /* Wait for an earlier IPI to finish. */
- if (!lapic_ipi_wait(BEFORE_SPIN))
- panic("ipi_nmi_selected: previous IPI has not cleared");
-
- lapic_ipi_raw(icrlo, cpu_apic_ids[cpu]);
- }
-}
-
int
-ipi_nmi_handler(void)
+ipi_nmi_handler()
{
- int cpumask = PCPU_GET(cpumask);
+ cpumask_t cpumask;
- if (!(ipi_nmi_pending & cpumask))
- return 1;
+ /*
+ * As long as there is not a simple way to know about a NMI's
+ * source, if the bitmask for the current CPU is present in
+ * the global pending bitword an IPI_STOP_HARD has been issued
+ * and should be handled.
+ */
+ cpumask = PCPU_GET(cpumask);
+ if ((ipi_nmi_pending & cpumask) == 0)
+ return (1);
atomic_clear_int(&ipi_nmi_pending, cpumask);
cpustop_handler();
- return 0;
+ return (0);
}
-#endif /* STOP_NMI */
-
/*
* Handle an IPI_STOP by saving our current context and spinning until we
* are resumed.
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index e967104..354d791 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/trap.c
@@ -211,13 +211,11 @@ trap(struct trapframe *frame)
type = frame->tf_trapno;
#ifdef SMP
-#ifdef STOP_NMI
/* Handler for NMI IPIs used for stopping CPUs. */
if (type == T_NMI) {
if (ipi_nmi_handler() == 0)
goto out;
}
-#endif /* STOP_NMI */
#endif /* SMP */
#ifdef KDB
diff --git a/sys/i386/include/apicvar.h b/sys/i386/include/apicvar.h
index a03c083..a13766f 100644
--- a/sys/i386/include/apicvar.h
+++ b/sys/i386/include/apicvar.h
@@ -100,11 +100,6 @@
* smp_ipi_mtx and waits for the completion of the IPI (Only one IPI user
* at a time) The second group uses a single interrupt and a bitmap to avoid
* redundant IPI interrupts.
- *
- * Right now IPI_STOP used by kdb shares the interrupt priority class with
- * the two IPI groups mentioned above. As such IPI_STOP may cause a deadlock.
- * Eventually IPI_STOP should use NMI IPIs - this would eliminate this and
- * other deadlocks caused by IPI_STOP.
*/
/* Interrupts for local APIC LVT entries other than the timer. */
@@ -134,6 +129,7 @@
#define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST)
#define IPI_STOP (APIC_IPI_INTS + 7) /* Stop CPU until restarted. */
+#define IPI_STOP_HARD (APIC_IPI_INTS + 8) /* Stop CPU with a NMI. */
#else /* XEN */
/* These are the normal i386 APIC definitions */
@@ -161,6 +157,7 @@
#define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST)
#define IPI_STOP (APIC_IPI_INTS + 7) /* Stop CPU until restarted. */
+#define IPI_STOP_HARD (APIC_IPI_INTS + 8) /* Stop CPU with a NMI. */
#endif /* XEN */
/*
diff --git a/sys/i386/include/smp.h b/sys/i386/include/smp.h
index 917c285..968cdb4 100644
--- a/sys/i386/include/smp.h
+++ b/sys/i386/include/smp.h
@@ -60,7 +60,8 @@ inthand_t
void cpu_add(u_int apic_id, char boot_cpu);
void cpustop_handler(void);
void init_secondary(void);
-void ipi_selected(u_int cpus, u_int ipi);
+int ipi_nmi_handler(void);
+void ipi_selected(cpumask_t cpus, u_int ipi);
void ipi_all_but_self(u_int ipi);
#ifndef XEN
void ipi_bitmap_handler(struct trapframe frame);
@@ -76,9 +77,6 @@ void smp_masked_invlpg_range(cpumask_t mask, vm_offset_t startva,
void smp_invltlb(void);
void smp_masked_invltlb(cpumask_t mask);
-#ifdef STOP_NMI
-int ipi_nmi_handler(void);
-#endif
#ifdef XEN
void ipi_to_irq_init(void);
diff --git a/sys/i386/xen/mp_machdep.c b/sys/i386/xen/mp_machdep.c
index 3aa03ce..bae07d4 100644
--- a/sys/i386/xen/mp_machdep.c
+++ b/sys/i386/xen/mp_machdep.c
@@ -90,8 +90,6 @@ __FBSDID("$FreeBSD$");
#include <xen/hypervisor.h>
#include <xen/interface/vcpu.h>
-#define stop_cpus_with_nmi 0
-
int mp_naps; /* # of Applications processors */
int boot_cpu_id = -1; /* designated BSP */
OpenPOWER on IntegriCloud