Rename the KDB_STOP_NMI kernel option to STOP_NMI and make it apply to all

IPI_STOP IPIs.
- Change the i386 and amd64 MD IPI code to send an NMI if STOP_NMI is
  enabled if an attempt is made to send an IPI_STOP IPI.  If the kernel
  option is enabled, there is also a sysctl to change the behavior at
  runtime (debug.stop_cpus_with_nmi which defaults to enabled).  This
  includes removing stop_cpus_nmi() and making ipi_nmi_selected() a
  private function for i386 and amd64.
- Fix ipi_all(), ipi_all_but_self(), and ipi_self() on i386 and amd64 to
  properly handle bitmapped IPIs as well as IPI_STOP IPIs when STOP_NMI is
  enabled.
- Fix ipi_nmi_handler() to execute the restart function on the first CPU
  that is restarted making use of atomic_readandclear() rather than
  assuming that the BSP is always included in the set of restarted CPUs.
  Also, the NMI handler didn't clear the function pointer meaning that
  subsequent stop and restarts could execute the function again.
- Define a new macro HAVE_STOPPEDPCBS on i386 and amd64 to control the use
  of stoppedpcbs[] and always enable it for i386 and amd64 instead of
  being dependent on KDB_STOP_NMI.  It works fine in both the NMI and
  non-NMI cases.
This commit is contained in:
John Baldwin 2005-10-24 21:04:19 +00:00
parent 301268b8ca
commit 58553b9925
15 changed files with 115 additions and 109 deletions

View File

@ -28,7 +28,6 @@
__FBSDID("$FreeBSD$");
#include "opt_cpu.h"
#include "opt_kdb.h"
#include "opt_kstack_pages.h"
#include "opt_mp_watchdog.h"
#include "opt_sched.h"
@ -113,10 +112,30 @@ volatile int smp_tlb_wait;
extern inthand_t IDTVEC(fast_syscall), IDTVEC(fast_syscall32);
#ifdef STOP_NMI
volatile cpumask_t ipi_nmi_pending;
static void ipi_nmi_selected(u_int32_t cpus);
#endif
/*
* 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;
/* used to hold the AP's until we are ready to release them */
@ -199,11 +218,6 @@ mp_topology(void)
smp_topology = &mp_top;
}
#ifdef KDB_STOP_NMI
volatile cpumask_t ipi_nmi_pending;
#endif
/*
* Calculate usable address in base memory for AP trampoline code.
*/
@ -944,6 +958,12 @@ ipi_selected(u_int32_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
CTR3(KTR_SMP, "%s: cpus: %x ipi: %x", __func__, cpus, ipi);
while ((cpu = ffs(cpus)) != 0) {
cpu--;
@ -974,6 +994,10 @@ void
ipi_all(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(all_cpus, ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_ALL);
}
@ -985,6 +1009,10 @@ void
ipi_all_but_self(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(PCPU_GET(other_cpus), ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS);
}
@ -996,11 +1024,15 @@ void
ipi_self(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(PCPU_GET(cpumask), ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_SELF);
}
#ifdef KDB_STOP_NMI
#ifdef STOP_NMI
/*
* send NMI IPI to selected CPUs
*/
@ -1040,8 +1072,9 @@ ipi_nmi_handler()
{
int cpu = PCPU_GET(cpuid);
int cpumask = PCPU_GET(cpumask);
void (*restartfunc)(void);
if (!(atomic_load_acq_int(&ipi_nmi_pending) & cpumask))
if (!(ipi_nmi_pending & cpumask))
return 1;
atomic_clear_int(&ipi_nmi_pending, cpumask);
@ -1052,19 +1085,21 @@ ipi_nmi_handler()
atomic_set_int(&stopped_cpus, cpumask);
/* Wait for restart */
while (!(atomic_load_acq_int(&started_cpus) & cpumask))
while (!(started_cpus & cpumask))
ia32_pause();
atomic_clear_int(&started_cpus, cpumask);
atomic_clear_int(&stopped_cpus, cpumask);
if (cpu == 0 && cpustop_restartfunc != NULL)
cpustop_restartfunc();
restartfunc = (void (*)(void))atomic_readandclear_long(
(u_long *)&cpustop_restartfunc);
if (restartfunc != NULL)
restartfunc();
return 0;
}
#endif /* KDB_STOP_NMI */
#endif /* STOP_NMI */
/*
* This is called once the rest of the system is up and running and we're

View File

@ -167,13 +167,13 @@ trap(frame)
PCPU_LAZY_INC(cnt.v_trap);
type = frame.tf_trapno;
#ifdef KDB_STOP_NMI
/* Handler for NMI IPIs used for debugging */
#ifdef STOP_NMI
/* Handler for NMI IPIs used for stopping CPUs. */
if (type == T_NMI) {
if (ipi_nmi_handler() == 0)
goto out;
}
#endif /* KDB_STOP_NMI */
#endif /* STOP_NMI */
#ifdef KDB
if (kdb_active) {

View File

@ -39,7 +39,7 @@ options MP_WATCHDOG
#
# Debugging options.
#
options KDB_STOP_NMI # Stop CPUS using NMI instead of IPI
options STOP_NMI # Stop CPUS using NMI instead of IPI

View File

@ -63,9 +63,8 @@ void smp_masked_invlpg_range(u_int mask, vm_offset_t startva,
void smp_invltlb(void);
void smp_masked_invltlb(u_int mask);
#ifdef KDB_STOP_NMI
int ipi_nmi_handler(void);
void ipi_nmi_selected(u_int32_t cpus);
#ifdef STOP_NMI
int ipi_nmi_handler(void);
#endif
#endif /* !LOCORE */

View File

@ -57,4 +57,4 @@ PSM_DEBUG opt_psm.h
DEV_ATPIC opt_atpic.h
# Debugging
KDB_STOP_NMI opt_kdb.h
STOP_NMI opt_cpu.h

View File

@ -161,5 +161,5 @@ DEV_NPX opt_npx.h
ASR_COMPAT opt_asr.h
# Debugging
KDB_STOP_NMI opt_kdb.h
STOP_NMI opt_cpu.h
NPX_DEBUG opt_npx.h

View File

@ -103,5 +103,5 @@ DEV_MECIA opt_mecia.h
DEV_NPX opt_npx.h
# Debugging
KDB_STOP_NMI opt_kdb.h
STOP_NMI opt_cpu.h
NPX_DEBUG opt_npx.h

View File

@ -56,7 +56,7 @@ options MP_WATCHDOG
# Debugging options.
#
options KDB_STOP_NMI # Stop CPUS using NMI instead of IPI
options STOP_NMI # Stop CPUS using NMI instead of IPI

View File

@ -28,7 +28,6 @@ __FBSDID("$FreeBSD$");
#include "opt_apic.h"
#include "opt_cpu.h"
#include "opt_kdb.h"
#include "opt_kstack_pages.h"
#include "opt_mp_watchdog.h"
#include "opt_sched.h"
@ -177,8 +176,10 @@ vm_offset_t smp_tlb_addr1;
vm_offset_t smp_tlb_addr2;
volatile int smp_tlb_wait;
#ifdef KDB_STOP_NMI
#ifdef STOP_NMI
volatile cpumask_t ipi_nmi_pending;
static void ipi_nmi_selected(u_int32_t cpus);
#endif
#ifdef COUNT_IPIS
@ -198,6 +199,20 @@ 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;
/* used to hold the AP's until we are ready to release them */
@ -1182,6 +1197,12 @@ ipi_selected(u_int32_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
CTR3(KTR_SMP, "%s: cpus: %x ipi: %x", __func__, cpus, ipi);
while ((cpu = ffs(cpus)) != 0) {
cpu--;
@ -1212,6 +1233,10 @@ void
ipi_all(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(all_cpus, ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_ALL);
}
@ -1223,6 +1248,10 @@ void
ipi_all_but_self(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(PCPU_GET(other_cpus), ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS);
}
@ -1234,11 +1263,15 @@ void
ipi_self(u_int ipi)
{
if (IPI_IS_BITMAPED(ipi) || (ipi == IPI_STOP && stop_cpus_with_nmi)) {
ipi_selected(PCPU_GET(cpumask), ipi);
return;
}
CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
lapic_ipi_vectored(ipi, APIC_IPI_DEST_SELF);
}
#ifdef KDB_STOP_NMI
#ifdef STOP_NMI
/*
* send NMI IPI to selected CPUs
*/
@ -1273,14 +1306,14 @@ ipi_nmi_selected(u_int32_t cpus)
}
}
int
ipi_nmi_handler()
{
int cpu = PCPU_GET(cpuid);
int cpumask = PCPU_GET(cpumask);
void (*restartfunc)(void);
if (!(atomic_load_acq_int(&ipi_nmi_pending) & cpumask))
if (!(ipi_nmi_pending & cpumask))
return 1;
atomic_clear_int(&ipi_nmi_pending, cpumask);
@ -1291,19 +1324,21 @@ ipi_nmi_handler()
atomic_set_int(&stopped_cpus, cpumask);
/* Wait for restart */
while (!(atomic_load_acq_int(&started_cpus) & cpumask))
while (!(started_cpus & cpumask))
ia32_pause();
atomic_clear_int(&started_cpus, cpumask);
atomic_clear_int(&stopped_cpus, cpumask);
if (cpu == 0 && cpustop_restartfunc != NULL)
cpustop_restartfunc();
restartfunc = (void (*)(void))atomic_readandclear_int(
(u_int *)&cpustop_restartfunc);
if (restartfunc != NULL)
restartfunc();
return 0;
}
#endif /* KDB_STOP_NMI */
#endif /* STOP_NMI */
/*
* This is called once the rest of the system is up and running and we're

View File

@ -185,13 +185,13 @@ trap(frame)
PCPU_LAZY_INC(cnt.v_trap);
type = frame.tf_trapno;
#ifdef KDB_STOP_NMI
/* Handler for NMI IPIs used for debugging */
#ifdef STOP_NMI
/* Handler for NMI IPIs used for stopping CPUs. */
if (type == T_NMI) {
if (ipi_nmi_handler() == 0)
goto out;
}
#endif /* KDB_STOP_NMI */
#endif /* STOP_NMI */
#ifdef KDB
if (kdb_active) {

View File

@ -79,9 +79,8 @@ void smp_masked_invlpg_range(u_int mask, vm_offset_t startva,
void smp_invltlb(void);
void smp_masked_invltlb(u_int mask);
#ifdef KDB_STOP_NMI
int ipi_nmi_handler(void);
void ipi_nmi_selected(u_int32_t cpus);
#ifdef STOP_NMI
int ipi_nmi_handler(void);
#endif
#endif /* !LOCORE */

View File

@ -42,16 +42,11 @@ __FBSDID("$FreeBSD$");
#include <machine/kdb.h>
#include <machine/pcb.h>
#ifdef KDB_STOP_NMI
#ifdef SMP
#if defined (__i386__) || defined(__amd64__)
#define HAVE_STOPPEDPCBS
#include <machine/smp.h>
#endif
/*
* KDB_STOP_NMI requires SMP to pick up the right dependencies
* (And isn't useful on UP anyway)
*/
#if defined(KDB_STOP_NMI) && !defined(SMP)
#error "options KDB_STOP_NMI" requires "options SMP"
#endif
int kdb_active = 0;
@ -91,19 +86,6 @@ static int kdb_stop_cpus = 1;
SYSCTL_INT(_debug_kdb, OID_AUTO, stop_cpus, CTLTYPE_INT | CTLFLAG_RW,
&kdb_stop_cpus, 0, "");
TUNABLE_INT("debug.kdb.stop_cpus", &kdb_stop_cpus);
#ifdef KDB_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 kdb_stop_cpus_with_nmi = 1;
SYSCTL_INT(_debug_kdb, OID_AUTO, stop_cpus_with_nmi, CTLTYPE_INT | CTLFLAG_RW,
&kdb_stop_cpus_with_nmi, 0, "");
TUNABLE_INT("debug.kdb.stop_cpus_with_nmi", &kdb_stop_cpus_with_nmi);
#endif /* KDB_STOP_NMI */
#endif
static int
@ -335,26 +317,24 @@ kdb_reenter(void)
struct pcb *
kdb_thr_ctx(struct thread *thr)
#ifdef KDB_STOP_NMI
{
#ifdef HAVE_STOPPEDPCBS
struct pcpu *pc;
u_int cpuid;
#endif
if (thr == curthread)
return (&kdb_pcb);
#ifdef HAVE_STOPPEDPCBS
SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
cpuid = pc->pc_cpuid;
if (pc->pc_curthread == thr && (atomic_load_acq_int(&stopped_cpus) & (1 << cpuid)))
if (pc->pc_curthread == thr && (stopped_cpus & (1 << cpuid)))
return (&stoppcbs[cpuid]);
}
#endif
return (thr->td_pcb);
}
#else
{
return ((thr == curthread) ? &kdb_pcb : thr->td_pcb);
}
#endif /* KDB_STOP_NMI */
struct thread *
kdb_thr_first(void)
@ -451,14 +431,7 @@ kdb_trap(int type, int code, struct trapframe *tf)
#ifdef SMP
if ((did_stop_cpus = kdb_stop_cpus) != 0)
{
#ifdef KDB_STOP_NMI
if(kdb_stop_cpus_with_nmi)
stop_cpus_nmi(PCPU_GET(other_cpus));
else
#endif /* KDB_STOP_NMI */
stop_cpus(PCPU_GET(other_cpus));
}
#endif
kdb_frame = tf;

View File

@ -35,8 +35,6 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_kdb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@ -256,36 +254,6 @@ stop_cpus(cpumask_t map)
return 1;
}
#ifdef KDB_STOP_NMI
int
stop_cpus_nmi(cpumask_t map)
{
int i;
if (!smp_started)
return 0;
CTR1(KTR_SMP, "stop_cpus(%x)", map);
/* send the stop IPI to all CPUs in map */
ipi_nmi_selected(map);
i = 0;
while ((atomic_load_acq_int(&stopped_cpus) & map) != map) {
/* spin */
i++;
#ifdef DIAGNOSTIC
if (i == 100000) {
printf("timeout stopping cpus\n");
break;
}
#endif
}
return 1;
}
#endif /* KDB_STOP_NMI */
/*
* Called by a CPU to restart stopped CPUs.
*

View File

@ -39,7 +39,7 @@ options MP_WATCHDOG
# Debugging options.
#
options KDB_STOP_NMI # Stop CPUS using NMI instead of IPI
options STOP_NMI # Stop CPUS using NMI instead of IPI

View File

@ -102,9 +102,6 @@ int stop_cpus(cpumask_t);
void smp_rendezvous_action(void);
extern struct mtx smp_ipi_mtx;
#ifdef KDB_STOP_NMI
int stop_cpus_nmi(cpumask_t);
#endif
#endif /* SMP */
void smp_rendezvous(void (*)(void *),
void (*)(void *),