- rewrite x86 nmi dispatcher so that establish and disesablish are safe

on a running system.
- adapt existing users of the api.  (elan)
- adapt tprof_pmi driver to use the api.
This commit is contained in:
yamt 2009-02-24 06:03:54 +00:00
parent 3076c08525
commit 4b64c2cb3d
10 changed files with 308 additions and 145 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: trap.c,v 1.53 2008/11/14 15:03:44 ad Exp $ */
/* $NetBSD: trap.c,v 1.54 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@ -68,16 +68,11 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.53 2008/11/14 15:03:44 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.54 2009/02/24 06:03:54 yamt Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_xen.h"
#if !defined(XEN)
#include "tprof.h"
#else /* !defined(XEN) */
#define NTPROF 0
#endif /* !defined(XEN) */
#include <sys/param.h>
#include <sys/systm.h>
@ -97,10 +92,6 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.53 2008/11/14 15:03:44 ad Exp $");
#include <uvm/uvm_extern.h>
#if NTPROF > 0
#include <x86/tprof.h>
#endif /* NTPROF > 0 */
#include <machine/cpufunc.h>
#include <machine/fpu.h>
#include <machine/psl.h>
@ -111,6 +102,8 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.53 2008/11/14 15:03:44 ad Exp $");
#include <machine/db_machdep.h>
#endif
#include <x86/nmi.h>
#ifndef XEN
#include "isa.h"
#endif
@ -595,10 +588,10 @@ faultcommon:
break;
case T_NMI:
#if NTPROF > 0
if (tprof_pmi_nmi(frame))
#if !defined(XEN)
if (nmi_dispatch(frame))
return;
#endif /* NTPROF > 0 */
#endif /* !defined(XEN) */
#if NISA > 0
#if defined(KGDB) || defined(DDB)
/* NMI can be hooked up to a pushbutton for debugging */

View File

@ -1,4 +1,4 @@
/* $NetBSD: trap.c,v 1.241 2008/10/15 06:51:17 wrstuden Exp $ */
/* $NetBSD: trap.c,v 1.242 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c) 1998, 2000, 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -68,7 +68,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.241 2008/10/15 06:51:17 wrstuden Exp $");
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.242 2009/02/24 06:03:54 yamt Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
@ -78,11 +78,6 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.241 2008/10/15 06:51:17 wrstuden Exp $");
#include "opt_kvm86.h"
#include "opt_kstack_dr0.h"
#include "opt_xen.h"
#if !defined(XEN)
#include "tprof.h"
#else /* defined(XEN) */
#define NTPROF 0
#endif /* defined(XEN) */
#include <sys/param.h>
#include <sys/systm.h>
@ -102,10 +97,6 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.241 2008/10/15 06:51:17 wrstuden Exp $");
#include <uvm/uvm_extern.h>
#if NTPROF > 0
#include <x86/tprof.h>
#endif /* NTPROF > 0 */
#include <machine/cpufunc.h>
#include <machine/psl.h>
#include <machine/reg.h>
@ -120,6 +111,8 @@ __KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.241 2008/10/15 06:51:17 wrstuden Exp $");
#include <machine/mca_machdep.h>
#endif
#include <x86/nmi.h>
#include "isa.h"
#ifdef KGDB
@ -768,12 +761,8 @@ copyfault:
break;
case T_NMI:
#if NTPROF > 0
if (tprof_pmi_nmi(frame))
return;
#endif /* NTPROF > 0 */
#if !defined(XEN)
if (nmi_dispatch())
if (nmi_dispatch(frame))
return;
#if (NISA > 0 || NMCA > 0)
#if defined(KGDB) || defined(DDB)

View File

@ -1,4 +1,4 @@
/* $NetBSD: elan520.c,v 1.37 2009/02/06 01:40:36 dyoung Exp $ */
/* $NetBSD: elan520.c,v 1.38 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
@ -40,7 +40,7 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.37 2009/02/06 01:40:36 dyoung Exp $");
__KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.38 2009/02/24 06:03:54 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -55,6 +55,8 @@ __KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.37 2009/02/06 01:40:36 dyoung Exp $");
#include <machine/bus.h>
#include <x86/nmi.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
@ -421,6 +423,13 @@ elanpar_intr(void *arg)
return 0;
}
static int
elanpar_nmi(const struct trapframe *tf, void *arg)
{
return elanpar_intr(arg);
}
static int
elanpex_intr(void *arg)
{
@ -521,6 +530,13 @@ elanpex_intr(void *arg)
return fatal ? 0 : (handled ? 1 : 0);
}
static int
elanpex_nmi(const struct trapframe *tf, void *arg)
{
return elanpex_intr(arg);
}
#define elansc_print_1(__dev, __sc, __reg) \
do { \
aprint_debug_dev(__dev, \
@ -1004,7 +1020,7 @@ elanpex_intr_establish(device_t self, struct elansc_softc *sc)
tgtirq |= MMCR_HBTGTIRQCTL_T_DPER_IRQ_ENB;
if (elansc_pcinmi) {
sc->sc_eih = nmi_establish(elanpex_intr, sc);
sc->sc_eih = nmi_establish(elanpex_nmi, sc);
/* Activate NMI instead of maskable interrupts for
* all PCI exceptions:
@ -1134,7 +1150,7 @@ elanpar_intr_establish(device_t self, struct elansc_softc *sc)
/* establish interrupt */
if (elansc_wpvnmi)
sc->sc_pih = nmi_establish(elanpar_intr, sc);
sc->sc_pih = nmi_establish(elanpar_nmi, sc);
else
sc->sc_pih = elansc_intr_establish(self, elanpar_intr, sc);

View File

@ -1,4 +1,4 @@
# $NetBSD: files.x86,v 1.45 2009/02/17 01:42:51 jmcneill Exp $
# $NetBSD: files.x86,v 1.46 2009/02/24 06:03:54 yamt Exp $
# options for MP configuration through the MP spec
defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI
@ -50,6 +50,7 @@ file arch/x86/x86/genfb_machdep.c
file arch/x86/x86/identcpu.c
file arch/x86/x86/i8259.c
file arch/x86/x86/intr.c
file arch/x86/x86/nmi.c
file arch/x86/x86/idt.c
file arch/x86/x86/ipi.c
file arch/x86/x86/msr_ipifuncs.c

View File

@ -1,4 +1,4 @@
/* $NetBSD: intr.h,v 1.35 2008/05/30 19:03:10 ad Exp $ */
/* $NetBSD: intr.h,v 1.36 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c) 1998, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -161,9 +161,6 @@ struct cpu_info;
struct pcibus_attach_args;
void intr_default_setup(void);
void *nmi_establish(int (*)(void *), void *);
bool nmi_disestablish(void *);
int nmi_dispatch(void);
int x86_nmi(void);
void *intr_establish(int, struct pic *, int, int, int, int (*)(void *), void *, bool);
void intr_disestablish(struct intrhand *);

View File

@ -0,0 +1,40 @@
/* $Id: nmi.h,v 1.1 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c)2009 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _X86_NMI_H_
#define _X86_NMI_H_
typedef struct nmi_handler nmi_handler_t;
struct trapframe;
nmi_handler_t *nmi_establish(int (*)(const struct trapframe *, void *), void *);
void nmi_disestablish(nmi_handler_t *);
int nmi_dispatch(const struct trapframe *);
void nmi_init(void);
#endif /* _X86_NMI_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tprof.h,v 1.1 2008/01/01 21:28:39 yamt Exp $ */
/* $NetBSD: tprof.h,v 1.2 2009/02/24 06:03:54 yamt Exp $ */
/*-
* Copyright (c)2008 YAMAMOTO Takashi,
@ -29,7 +29,6 @@
#ifndef _X86_TPROF_H_
#define _X86_TPROF_H_
struct trapframe;
int tprof_pmi_nmi(const struct trapframe *);
/* XXX nothing */
#endif /* _X86_TPROF_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: intr.c,v 1.58 2008/12/17 20:51:33 cegger Exp $ */
/* $NetBSD: intr.c,v 1.59 2009/02/24 06:03:55 yamt Exp $ */
/*-
* Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
@ -133,7 +133,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.58 2008/12/17 20:51:33 cegger Exp $");
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.59 2009/02/24 06:03:55 yamt Exp $");
#include "opt_multiprocessor.h"
#include "opt_acpi.h"
@ -144,11 +144,13 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.58 2008/12/17 20:51:33 cegger Exp $");
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/intr.h>
#include <sys/cpu.h>
#include <sys/atomic.h>
#include <sys/xcall.h>
#include <uvm/uvm_extern.h>
@ -190,7 +192,6 @@ static int intr_find_pcibridge(int, pcitag_t *, pci_chipset_tag_t *);
#endif
kmutex_t x86_intr_lock;
bool x86_intr_lock_initted;
/*
* Fill in default interrupt table (in case of spurious interrupt
@ -217,61 +218,6 @@ intr_default_setup(void)
i8259_default_setup();
}
struct nmi_handler {
int (*n_func)(void *);
void *n_arg;
SLIST_ENTRY(nmi_handler) n_next;
};
SLIST_HEAD(nmi_handler_head, nmi_handler) nmi_handlers =
SLIST_HEAD_INITIALIZER(nmi_handler_head);
void *
nmi_establish(int (*func)(void *), void *arg)
{
struct nmi_handler *n;
n = malloc(sizeof(*n), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
if (n == NULL)
return NULL;
n->n_func = func;
n->n_arg = arg;
SLIST_INSERT_HEAD(&nmi_handlers, n, n_next);
KASSERT(SLIST_FIRST(&nmi_handlers) == n);
return n;
}
bool
nmi_disestablish(void *n0)
{
struct nmi_handler *n;
SLIST_FOREACH(n, &nmi_handlers, n_next) {
if (n == n0)
break;
}
if (n == NULL)
return false;
SLIST_REMOVE(&nmi_handlers, n, nmi_handler, n_next);
free(n, M_DEVBUF);
return true;
}
int
nmi_dispatch(void)
{
int handled = 0;
struct nmi_handler *n;
SLIST_FOREACH(n, &nmi_handlers, n_next) {
if ((*n->n_func)(n->n_arg))
handled = 1;
}
return handled;
}
/*
* Handle a NMI, possibly a machine check.
* return true to panic system, false to ignore.

169
sys/arch/x86/x86/nmi.c Normal file
View File

@ -0,0 +1,169 @@
/* $Id: nmi.c,v 1.1 2009/02/24 06:03:55 yamt Exp $ */
/*-
* Copyright (c)2009 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nmi.c,v 1.1 2009/02/24 06:03:55 yamt Exp $");
/*
* nmi dispatcher.
*
* XXX no need to be nmi-specific.
* actual assumptions are:
* - dispatch() is called with preemption disabled.
* - handlers never block.
* - establish() and disestablish() are called within a thread context.
* (thus can block)
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/xcall.h>
#include <x86/nmi.h>
struct nmi_handler {
int (*n_func)(const struct trapframe *, void *);
void *n_arg;
struct nmi_handler *n_next;
};
static kmutex_t nmi_list_lock; /* serialize establish and disestablish */
static nmi_handler_t *nmi_handlers; /* list of handlers */
/*
* nmi_establish: establish an nmi handler
*
* => can block.
* => returns an opaque handle.
*/
nmi_handler_t *
nmi_establish(int (*func)(const struct trapframe *, void *), void *arg)
{
struct nmi_handler *n;
n = kmem_alloc(sizeof(*n), KM_SLEEP);
if (n == NULL) {
return NULL;
}
n->n_func = func;
n->n_arg = arg;
/*
* put it into the list.
*/
mutex_enter(&nmi_list_lock);
n->n_next = nmi_handlers;
membar_producer(); /* n->n_next should be visible before nmi_handlers */
nmi_handlers = n; /* atomic store */
mutex_exit(&nmi_list_lock);
return n;
}
/*
* nmi_disestablish: disestablish an nmi handler.
*
* => can block.
* => take an opaque handle. it must be one returned by nmi_establish.
*/
void
nmi_disestablish(nmi_handler_t *handle)
{
nmi_handler_t *n;
nmi_handler_t **pp;
KASSERT(handle != NULL);
/*
* remove the handler from the list.
*/
mutex_enter(&nmi_list_lock);
for (pp = &nmi_handlers, n = *pp; n != NULL; n = *pp) {
if (n == handle) {
break;
}
pp = &n->n_next;
}
#if defined(DIAGNOSTIC)
if (n == NULL) {
mutex_exit(&nmi_list_lock);
panic("%s: invalid handle %p", __func__, handle);
}
#endif /* defined(DIAGNOSTIC) */
*pp = n->n_next; /* atomic store */
mutex_exit(&nmi_list_lock); /* mutex_exit implies a store fence */
/*
* before freeing 'n', ensure that no other cpus are
* in the middle of nmi_dispatch.
*/
if (!mp_online) {
uint64_t h;
h = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL);
xc_wait(h);
}
kmem_free(n, sizeof(*n));
}
/*
* nmi_dispatch: dispatch an nmi.
*
* => called by interrupts, thus preempt disabled.
*/
int
nmi_dispatch(const struct trapframe *tf)
{
const struct nmi_handler *n;
int handled = 0;
for (n = nmi_handlers; /* atomic load */
n != NULL;
n = n->n_next) { /* atomic load */
handled |= (*n->n_func)(tf, n->n_arg);
}
return handled;
}
void
nmi_init(void)
{
mutex_init(&nmi_list_lock, MUTEX_DEFAULT, IPL_NONE);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: tprof_pmi.c,v 1.3 2008/05/11 22:51:02 yamt Exp $ */
/* $NetBSD: tprof_pmi.c,v 1.4 2009/02/24 06:03:55 yamt Exp $ */
/*-
* Copyright (c)2008 YAMAMOTO Takashi,
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.3 2008/05/11 22:51:02 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.4 2009/02/24 06:03:55 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -39,6 +39,8 @@ __KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.3 2008/05/11 22:51:02 yamt Exp $");
#include <dev/tprof/tprof.h>
#include <x86/tprof.h>
#include <x86/nmi.h>
#include <machine/db_machdep.h> /* PC_REGS */
#include <machine/cpuvar.h> /* cpu_vendor */
#include <machine/cputypes.h> /* CPUVENDER_* */
@ -99,6 +101,8 @@ static uint64_t counter_val = 5000000;
static uint64_t counter_reset_val;
static uint32_t tprof_pmi_lapic_saved[MAXCPUS];
static nmi_handler_t *tprof_pmi_nmi_handle;
static void
tprof_pmi_start_cpu(void *arg1, void *arg2)
{
@ -150,55 +154,16 @@ tprof_pmi_stop_cpu(void *arg1, void *arg2)
i82489_writereg(LAPIC_PCINT, tprof_pmi_lapic_saved[cpu_index(ci)]);
}
uint64_t
tprof_backend_estimate_freq(void)
{
uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
uint64_t freq = 10000;
counter_val = cpufreq / freq;
if (counter_val == 0) {
counter_val = UINT64_C(4000000000) / freq;
return freq;
}
return freq;
}
int
tprof_backend_start(void)
{
struct cpu_info * const ci = curcpu();
uint64_t xc;
if (!(cpu_vendor == CPUVENDOR_INTEL &&
CPUID2FAMILY(ci->ci_signature) == 15)) {
return ENOTSUP;
}
counter_reset_val = - counter_val + 1;
xc = xc_broadcast(0, tprof_pmi_start_cpu, NULL, NULL);
xc_wait(xc);
return 0;
}
void
tprof_backend_stop(void)
{
uint64_t xc;
xc = xc_broadcast(0, tprof_pmi_stop_cpu, NULL, NULL);
xc_wait(xc);
}
int
tprof_pmi_nmi(const struct trapframe *tf)
static int
tprof_pmi_nmi(const struct trapframe *tf, void *dummy)
{
struct cpu_info * const ci = curcpu();
const struct msrs *msr;
uint32_t pcint;
uint64_t cccr;
KASSERT(dummy == NULL);
if (ci->ci_smtid >= 2) {
/* not ours */
return 0;
@ -226,3 +191,51 @@ tprof_pmi_nmi(const struct trapframe *tf)
return 1;
}
uint64_t
tprof_backend_estimate_freq(void)
{
uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
uint64_t freq = 10000;
counter_val = cpufreq / freq;
if (counter_val == 0) {
counter_val = UINT64_C(4000000000) / freq;
return freq;
}
return freq;
}
int
tprof_backend_start(void)
{
struct cpu_info * const ci = curcpu();
uint64_t xc;
if (!(cpu_vendor == CPUVENDOR_INTEL &&
CPUID2FAMILY(ci->ci_signature) == 15)) {
return ENOTSUP;
}
KASSERT(tprof_pmi_nmi_handle == NULL);
tprof_pmi_nmi_handle = nmi_establish(tprof_pmi_nmi, NULL);
counter_reset_val = - counter_val + 1;
xc = xc_broadcast(0, tprof_pmi_start_cpu, NULL, NULL);
xc_wait(xc);
return 0;
}
void
tprof_backend_stop(void)
{
uint64_t xc;
xc = xc_broadcast(0, tprof_pmi_stop_cpu, NULL, NULL);
xc_wait(xc);
KASSERT(tprof_pmi_nmi_handle != NULL);
nmi_disestablish(tprof_pmi_nmi_handle);
tprof_pmi_nmi_handle = NULL;
}