diff options
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/subr_kdb.c | 50 | ||||
-rw-r--r-- | sys/kern/subr_smp.c | 29 |
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. |