summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/subr_kdb.c50
-rw-r--r--sys/kern/subr_smp.c29
2 files changed, 79 insertions, 0 deletions
diff --git a/sys/kern/subr_kdb.c b/sys/kern/subr_kdb.c
index 54274db..5726a84 100644
--- a/sys/kern/subr_kdb.c
+++ b/sys/kern/subr_kdb.c
@@ -40,6 +40,18 @@ __FBSDID("$FreeBSD$");
#include <machine/kdb.h>
#include <machine/pcb.h>
+#ifdef KDB_STOP_NMI
+#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;
void *kdb_jmpbufp = NULL;
struct kdb_dbbe *kdb_dbbe = NULL;
@@ -77,6 +89,19 @@ 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 = 0;
+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
@@ -308,9 +333,27 @@ kdb_reenter(void)
struct pcb *
kdb_thr_ctx(struct thread *thr)
+#ifdef KDB_STOP_NMI
+{
+ u_int cpuid;
+ struct pcpu *pc;
+
+ if (thr == curthread)
+ return &kdb_pcb;
+
+ SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
+ cpuid = pc->pc_cpuid;
+ if (pc->pc_curthread == thr && (atomic_load_acq_int(&stopped_cpus) & (1 << cpuid)))
+ return &stoppcbs[cpuid];
+ }
+
+ return thr->td_pcb;
+}
+#else
{
return ((thr == curthread) ? &kdb_pcb : thr->td_pcb);
}
+#endif /* KDB_STOP_NMI */
struct thread *
kdb_thr_first(void)
@@ -407,7 +450,14 @@ 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;
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c
index aed3118..3d08ad8 100644
--- a/sys/kern/subr_smp.c
+++ b/sys/kern/subr_smp.c
@@ -254,6 +254,35 @@ 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.
OpenPOWER on IntegriCloud