mirror of
https://github.com/opnsense/src.git
synced 2026-06-07 07:42:26 -04:00
x86: Refactor kernel-mode NMI handling
This refactor aims to add the ability to share performance counter interrupts by refactoring the kernel-mode NMI handler. The handler now allows multiple drivers to service the same interrupt (e.g. hwpmc(4) and hwt(4)'s Intel Processor Trace backend). Reviewed by: kib, avg Differential Revision: https://reviews.freebsd.org/D46421
This commit is contained in:
parent
74ecdf86d8
commit
459dc42787
4 changed files with 104 additions and 53 deletions
|
|
@ -230,38 +230,22 @@ trap(struct trapframe *frame)
|
|||
VM_CNT_INC(v_trap);
|
||||
type = frame->tf_trapno;
|
||||
|
||||
#ifdef SMP
|
||||
/* Handler for NMI IPIs used for stopping CPUs. */
|
||||
if (type == T_NMI && ipi_nmi_handler() == 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef KDB
|
||||
if (kdb_active) {
|
||||
kdb_reenter();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (type == T_NMI) {
|
||||
nmi_handle_intr(frame);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == T_RESERVED) {
|
||||
trap_fatal(frame, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == T_NMI) {
|
||||
#ifdef HWPMC_HOOKS
|
||||
/*
|
||||
* CPU PMCs interrupt using an NMI. If the PMC module is
|
||||
* active, pass the 'rip' value to the PMC module's interrupt
|
||||
* handler. A non-zero return value from the handler means that
|
||||
* the NMI was consumed by it and we can return immediately.
|
||||
*/
|
||||
if (pmc_intr != NULL &&
|
||||
(*pmc_intr)(frame) != 0)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((frame->tf_rflags & PSL_I) == 0) {
|
||||
/*
|
||||
* Buggy application or kernel code has disabled
|
||||
|
|
@ -392,10 +376,6 @@ trap(struct trapframe *frame)
|
|||
signo = SIGFPE;
|
||||
break;
|
||||
|
||||
case T_NMI:
|
||||
nmi_handle_intr(type, frame);
|
||||
return;
|
||||
|
||||
case T_OFLOW: /* integer overflow fault */
|
||||
ucode = FPE_INTOVF;
|
||||
signo = SIGFPE;
|
||||
|
|
@ -619,10 +599,6 @@ trap(struct trapframe *frame)
|
|||
return;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case T_NMI:
|
||||
nmi_handle_intr(type, frame);
|
||||
return;
|
||||
}
|
||||
|
||||
trap_fatal(frame, 0);
|
||||
|
|
|
|||
|
|
@ -237,12 +237,6 @@ trap(struct trapframe *frame)
|
|||
KASSERT((read_eflags() & PSL_I) == 0,
|
||||
("trap: interrupts enabled, type %d frame %p", type, frame));
|
||||
|
||||
#ifdef SMP
|
||||
/* Handler for NMI IPIs used for stopping CPUs. */
|
||||
if (type == T_NMI && ipi_nmi_handler() == 0)
|
||||
return;
|
||||
#endif /* SMP */
|
||||
|
||||
#ifdef KDB
|
||||
if (kdb_active) {
|
||||
kdb_reenter();
|
||||
|
|
@ -251,24 +245,14 @@ trap(struct trapframe *frame)
|
|||
#endif
|
||||
trap_check_kstack();
|
||||
|
||||
if (type == T_RESERVED) {
|
||||
trap_fatal(frame, 0);
|
||||
if (type == T_NMI) {
|
||||
nmi_handle_intr(frame);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == T_NMI) {
|
||||
#ifdef HWPMC_HOOKS
|
||||
/*
|
||||
* CPU PMCs interrupt using an NMI so we check for that first.
|
||||
* If the HWPMC module is active, 'pmc_hook' will point to
|
||||
* the function to be called. A non-zero return value from the
|
||||
* hook means that the NMI was consumed by it and that we can
|
||||
* return immediately.
|
||||
*/
|
||||
if (pmc_intr != NULL &&
|
||||
(*pmc_intr)(frame) != 0)
|
||||
return;
|
||||
#endif
|
||||
if (type == T_RESERVED) {
|
||||
trap_fatal(frame, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == T_MCHK) {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ void zenbleed_sanitize_enable(void);
|
|||
void zenbleed_check_and_apply(bool all_cpus);
|
||||
void nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame);
|
||||
void nmi_call_kdb_smp(u_int type, struct trapframe *frame);
|
||||
void nmi_handle_intr(u_int type, struct trapframe *frame);
|
||||
void nmi_register_handler(int (*handler)(struct trapframe *));
|
||||
void nmi_remove_handler(int (*handler)(struct trapframe *));
|
||||
void nmi_handle_intr(struct trapframe *frame);
|
||||
void pagecopy(void *from, void *to);
|
||||
void printcpuinfo(void);
|
||||
int pti_get_default(void);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#include <machine/cputypes.h>
|
||||
#include <machine/specialreg.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/trap.h>
|
||||
#include <machine/tss.h>
|
||||
#ifdef SMP
|
||||
#include <machine/smp.h>
|
||||
|
|
@ -885,17 +886,105 @@ nmi_call_kdb(u_int cpu, u_int type, struct trapframe *frame)
|
|||
panic("NMI");
|
||||
}
|
||||
|
||||
/*
|
||||
* Dynamically registered NMI handlers.
|
||||
*/
|
||||
struct nmi_handler {
|
||||
int running;
|
||||
int (*func)(struct trapframe *);
|
||||
struct nmi_handler *next;
|
||||
};
|
||||
static struct nmi_handler *nmi_handlers_head = NULL;
|
||||
MALLOC_DEFINE(M_NMI, "NMI handlers",
|
||||
"List entries for dynamically registered NMI handlers");
|
||||
|
||||
void
|
||||
nmi_handle_intr(u_int type, struct trapframe *frame)
|
||||
nmi_register_handler(int (*handler)(struct trapframe *))
|
||||
{
|
||||
struct nmi_handler *hp;
|
||||
int (*hpf)(struct trapframe *);
|
||||
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&nmi_handlers_head);
|
||||
while (hp != NULL) {
|
||||
hpf = hp->func;
|
||||
MPASS(hpf != handler);
|
||||
if (hpf == NULL &&
|
||||
atomic_cmpset_ptr((volatile uintptr_t *)&hp->func,
|
||||
(uintptr_t)NULL, (uintptr_t)handler) != 0) {
|
||||
hp->running = 0;
|
||||
return;
|
||||
}
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&hp->next);
|
||||
}
|
||||
hp = malloc(sizeof(struct nmi_handler), M_NMI, M_WAITOK | M_ZERO);
|
||||
hp->func = handler;
|
||||
hp->next = nmi_handlers_head;
|
||||
while (atomic_fcmpset_rel_ptr(
|
||||
(volatile uintptr_t *)&nmi_handlers_head,
|
||||
(uintptr_t *)&hp->next, (uintptr_t)hp) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
nmi_remove_handler(int (*handler)(struct trapframe *))
|
||||
{
|
||||
struct nmi_handler *hp;
|
||||
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&nmi_handlers_head);
|
||||
while (hp != NULL) {
|
||||
if (hp->func == handler) {
|
||||
hp->func = NULL;
|
||||
/* Wait for the handler to exit before returning. */
|
||||
while (atomic_load_int(&hp->running) != 0)
|
||||
cpu_spinwait();
|
||||
return;
|
||||
}
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&hp->next);
|
||||
}
|
||||
|
||||
panic("%s: attempting to remove an unregistered NMI handler %p\n",
|
||||
__func__, handler);
|
||||
}
|
||||
|
||||
void
|
||||
nmi_handle_intr(struct trapframe *frame)
|
||||
{
|
||||
int (*func)(struct trapframe *);
|
||||
struct nmi_handler *hp;
|
||||
bool handled;
|
||||
|
||||
#ifdef SMP
|
||||
/* Handler for NMI IPIs used for stopping CPUs. */
|
||||
if (ipi_nmi_handler() == 0)
|
||||
return;
|
||||
#endif
|
||||
handled = false;
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&nmi_handlers_head);
|
||||
while (hp != NULL) {
|
||||
func = hp->func;
|
||||
if (func != NULL) {
|
||||
atomic_add_int(&hp->running, 1);
|
||||
if (func(frame) != 0)
|
||||
handled = true;
|
||||
atomic_subtract_int(&hp->running, 1);
|
||||
}
|
||||
hp = (struct nmi_handler *)atomic_load_acq_ptr(
|
||||
(uintptr_t *)&hp->next);
|
||||
}
|
||||
if (handled)
|
||||
return;
|
||||
#ifdef SMP
|
||||
if (nmi_is_broadcast) {
|
||||
nmi_call_kdb_smp(type, frame);
|
||||
nmi_call_kdb_smp(T_NMI, frame);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
nmi_call_kdb(PCPU_GET(cpuid), type, frame);
|
||||
nmi_call_kdb(PCPU_GET(cpuid), T_NMI, frame);
|
||||
}
|
||||
|
||||
static int hw_ibrs_active;
|
||||
|
|
|
|||
Loading…
Reference in a new issue