svn commit: r196223 - in stable/8/sys: amd64/amd64 amd64/include dev/hwpmc i386/i386 i386/include

John Baldwin jhb at FreeBSD.org
Fri Aug 14 20:57:22 UTC 2009


Author: jhb
Date: Fri Aug 14 20:57:21 2009
New Revision: 196223
URL: http://svn.freebsd.org/changeset/base/196223

Log:
  Adjust the handling of the local APIC PMC interrupt vector:
  - Provide lapic_disable_pmc(), lapic_enable_pmc(), and lapic_reenable_pmc()
    routines in the local APIC code that the hwpmc(4) driver can use to
    manage the local APIC PMC interrupt vector.
  - Do not enable the local APIC PMC interrupt vector by default when
    HWPMC_HOOKS is enabled.  Instead, the hwpmc(4) driver explicitly
    enables the interrupt when it is succesfully initialized and disables
    the interrupt when it is unloaded.  This avoids enabling the interrupt
    on unsupported CPUs which may result in spurious NMIs.
  
  Reported by:	rnoland
  Reviewed by:	jkoshy
  Approved by:	re (kib)
  MFC after:	2 weeks

Modified:
  stable/8/sys/amd64/amd64/local_apic.c
  stable/8/sys/amd64/include/apicvar.h
  stable/8/sys/amd64/include/pmc_mdep.h
  stable/8/sys/dev/hwpmc/hwpmc_core.c
  stable/8/sys/dev/hwpmc/hwpmc_piv.c
  stable/8/sys/dev/hwpmc/hwpmc_ppro.c
  stable/8/sys/dev/hwpmc/hwpmc_x86.c
  stable/8/sys/i386/i386/local_apic.c
  stable/8/sys/i386/include/apicvar.h
  stable/8/sys/i386/include/pmc_mdep.h

Modified: stable/8/sys/amd64/amd64/local_apic.c
==============================================================================
--- stable/8/sys/amd64/amd64/local_apic.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/amd64/amd64/local_apic.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -123,7 +123,7 @@ static struct lvt lvts[LVT_MAX + 1] = {
 	{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 },	/* LINT1: NMI */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_TIMER_INT },	/* Timer */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_ERROR_INT },	/* Error */
-	{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 },	/* PMC */
+	{ 1, 1, 1, 1, APIC_LVT_DM_NMI, 0 },	/* PMC */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_THERMAL_INT },	/* Thermal */
 };
 
@@ -305,11 +305,9 @@ lapic_setup(int boot)
 	lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0);
 	lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1);
 
-#ifdef	HWPMC_HOOKS
 	/* Program the PMC LVT entry if present. */
 	if (maxlvt >= LVT_PMC)
 		lapic->lvt_pcint = lvt_mode(la, LVT_PMC, lapic->lvt_pcint);
-#endif
 
 	/* Program timer LVT and setup handler. */
 	lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer);
@@ -332,6 +330,88 @@ lapic_setup(int boot)
 	intr_restore(eflags);
 }
 
+void
+lapic_reenable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	uint32_t value;
+
+	value =  lapic->lvt_pcint;
+	value &= ~APIC_LVT_M;
+	lapic->lvt_pcint = value;
+#endif
+}
+
+#ifdef HWPMC_HOOKS
+static void
+lapic_update_pmc(void *dummy)
+{
+	struct lapic *la;
+
+	la = &lapics[lapic_id()];
+	lapic->lvt_pcint = lvt_mode(la, LVT_PMC, lapic->lvt_pcint);
+}
+#endif
+
+int
+lapic_enable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	u_int32_t maxlvt;
+
+	/* Fail if the local APIC is not present. */
+	if (lapic == NULL)
+		return (0);
+
+	/* Fail if the PMC LVT is not present. */
+	maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
+	if (maxlvt < LVT_PMC)
+		return (0);
+
+	lvts[LVT_PMC].lvt_masked = 0;
+
+#ifdef SMP
+	/*
+	 * If hwpmc was loaded at boot time then the APs may not be
+	 * started yet.  In that case, don't forward the request to
+	 * them as they will program the lvt when they start.
+	 */
+	if (smp_started)
+		smp_rendezvous(NULL, lapic_update_pmc, NULL, NULL);
+	else
+#endif
+		lapic_update_pmc(NULL);
+	return (1);
+#else
+	return (0);
+#endif
+}
+
+void
+lapic_disable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	u_int32_t maxlvt;
+
+	/* Fail if the local APIC is not present. */
+	if (lapic == NULL)
+		return;
+
+	/* Fail if the PMC LVT is not present. */
+	maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
+	if (maxlvt < LVT_PMC)
+		return;
+
+	lvts[LVT_PMC].lvt_masked = 1;
+
+#ifdef SMP
+	/* The APs should always be started when hwpmc is unloaded. */
+	KASSERT(mp_ncpus == 1 || smp_started, ("hwpmc unloaded too early"));
+#endif
+	smp_rendezvous(NULL, lapic_update_pmc, NULL, NULL);
+#endif
+}
+
 /*
  * Called by cpu_initclocks() on the BSP to setup the local APIC timer so
  * that it can drive hardclock, statclock, and profclock.  This function

Modified: stable/8/sys/amd64/include/apicvar.h
==============================================================================
--- stable/8/sys/amd64/include/apicvar.h	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/amd64/include/apicvar.h	Fri Aug 14 20:57:21 2009	(r196223)
@@ -201,7 +201,9 @@ int	ioapic_set_triggermode(void *cookie,
 int	ioapic_set_smi(void *cookie, u_int pin);
 void	lapic_create(u_int apic_id, int boot_cpu);
 void	lapic_disable(void);
+void	lapic_disable_pmc(void);
 void	lapic_dump(const char *str);
+int	lapic_enable_pmc(void);
 void	lapic_eoi(void);
 u_int	lapic_error(void);
 int	lapic_id(void);
@@ -212,6 +214,7 @@ void	lapic_ipi_vectored(u_int vector, in
 int	lapic_ipi_wait(int delay);
 void	lapic_handle_intr(int vector, struct trapframe *frame);
 void	lapic_handle_timer(struct trapframe *frame);
+void	lapic_reenable_pmc(void);
 void	lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
 int	lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
 int	lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);

Modified: stable/8/sys/amd64/include/pmc_mdep.h
==============================================================================
--- stable/8/sys/amd64/include/pmc_mdep.h	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/amd64/include/pmc_mdep.h	Fri Aug 14 20:57:21 2009	(r196223)
@@ -115,7 +115,6 @@ union pmc_md_pmc {
  */
 
 void	start_exceptions(void), end_exceptions(void);
-void	pmc_x86_lapic_enable_pmc_interrupt(void);
 
 struct pmc_mdep *pmc_amd_initialize(void);
 void	pmc_amd_finalize(struct pmc_mdep *_md);

Modified: stable/8/sys/dev/hwpmc/hwpmc_core.c
==============================================================================
--- stable/8/sys/dev/hwpmc/hwpmc_core.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/dev/hwpmc/hwpmc_core.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -32,10 +32,13 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/bus.h>
 #include <sys/pmc.h>
 #include <sys/pmckern.h>
 #include <sys/systm.h>
 
+#include <machine/intr_machdep.h>
+#include <machine/apicvar.h>
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
 #include <machine/specialreg.h>
@@ -1771,7 +1774,7 @@ core_intr(int cpu, struct trapframe *tf)
 	}
 
 	if (found_interrupt)
-		pmc_x86_lapic_enable_pmc_interrupt();
+		lapic_reenable_pmc();
 
 	atomic_add_int(found_interrupt ? &pmc_stats.pm_intr_processed :
 	    &pmc_stats.pm_intr_ignored, 1);
@@ -1895,7 +1898,7 @@ core2_intr(int cpu, struct trapframe *tf
 	    (uintmax_t) rdmsr(IA_GLOBAL_OVF_CTRL));
 
 	if (found_interrupt)
-		pmc_x86_lapic_enable_pmc_interrupt();
+		lapic_reenable_pmc();
 
 	atomic_add_int(found_interrupt ? &pmc_stats.pm_intr_processed :
 	    &pmc_stats.pm_intr_ignored, 1);

Modified: stable/8/sys/dev/hwpmc/hwpmc_piv.c
==============================================================================
--- stable/8/sys/dev/hwpmc/hwpmc_piv.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/dev/hwpmc/hwpmc_piv.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -32,6 +32,7 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/bus.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/pmc.h>
@@ -39,6 +40,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/smp.h>
 #include <sys/systm.h>
 
+#include <machine/intr_machdep.h>
+#include <machine/apicvar.h>
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
 #include <machine/cputypes.h>
@@ -1537,7 +1540,7 @@ p4_intr(int cpu, struct trapframe *tf)
 	 */
 
 	if (did_interrupt)
-		pmc_x86_lapic_enable_pmc_interrupt();
+		lapic_reenable_pmc();
 
 	atomic_add_int(did_interrupt ? &pmc_stats.pm_intr_processed :
 	    &pmc_stats.pm_intr_ignored, 1);

Modified: stable/8/sys/dev/hwpmc/hwpmc_ppro.c
==============================================================================
--- stable/8/sys/dev/hwpmc/hwpmc_ppro.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/dev/hwpmc/hwpmc_ppro.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -32,6 +32,7 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/bus.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/pmc.h>
@@ -39,6 +40,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/smp.h>
 #include <sys/systm.h>
 
+#include <machine/intr_machdep.h>
+#include <machine/apicvar.h>
 #include <machine/cpu.h>
 #include <machine/cpufunc.h>
 #include <machine/cputypes.h>
@@ -718,7 +721,7 @@ p6_intr(int cpu, struct trapframe *tf)
 	 * unmasked after a PMC interrupt.
 	 */
 	if (retval)
-		pmc_x86_lapic_enable_pmc_interrupt();
+		lapic_reenable_pmc();
 
 	atomic_add_int(retval ? &pmc_stats.pm_intr_processed :
 	    &pmc_stats.pm_intr_ignored, 1);

Modified: stable/8/sys/dev/hwpmc/hwpmc_x86.c
==============================================================================
--- stable/8/sys/dev/hwpmc/hwpmc_x86.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/dev/hwpmc/hwpmc_x86.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -39,7 +39,8 @@ __FBSDID("$FreeBSD$");
 
 #include <machine/cpu.h>
 #include <machine/cputypes.h>
-#include <machine/apicreg.h>
+#include <machine/intr_machdep.h>
+#include <machine/apicvar.h>
 #include <machine/pmc_mdep.h>
 #include <machine/md_var.h>
 
@@ -47,18 +48,6 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_param.h>
 #include <vm/pmap.h>
 
-extern volatile lapic_t *lapic;
-
-void
-pmc_x86_lapic_enable_pmc_interrupt(void)
-{
-	uint32_t value;
-
-	value =  lapic->lvt_pcint;
-	value &= ~APIC_LVT_M;
-	lapic->lvt_pcint = value;
-}
-
 /*
  * Attempt to walk a user call stack using a too-simple algorithm.
  * In the general case we need unwind information associated with
@@ -252,16 +241,15 @@ pmc_md_initialize()
 	struct pmc_mdep *md;
 
 	/* determine the CPU kind */
-	md = NULL;
 	if (cpu_vendor_id == CPU_VENDOR_AMD)
 		md = pmc_amd_initialize();
 	else if (cpu_vendor_id == CPU_VENDOR_INTEL)
 		md = pmc_intel_initialize();
 	else
-		KASSERT(0, ("[x86,%d] Unknown vendor", __LINE__));
+		return (NULL);
 
 	/* disallow sampling if we do not have an LAPIC */
-	if (md != NULL && lapic == NULL)
+	if (!lapic_enable_pmc())
 		for (i = 1; i < md->pmd_nclass; i++)
 			md->pmd_classdep[i].pcd_caps &= ~PMC_CAP_INTERRUPT;
 
@@ -271,6 +259,8 @@ pmc_md_initialize()
 void
 pmc_md_finalize(struct pmc_mdep *md)
 {
+
+	lapic_disable_pmc();
 	if (cpu_vendor_id == CPU_VENDOR_AMD)
 		pmc_amd_finalize(md);
 	else if (cpu_vendor_id == CPU_VENDOR_INTEL)

Modified: stable/8/sys/i386/i386/local_apic.c
==============================================================================
--- stable/8/sys/i386/i386/local_apic.c	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/i386/i386/local_apic.c	Fri Aug 14 20:57:21 2009	(r196223)
@@ -123,7 +123,7 @@ static struct lvt lvts[LVT_MAX + 1] = {
 	{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 },	/* LINT1: NMI */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_TIMER_INT },	/* Timer */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_ERROR_INT },	/* Error */
-	{ 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 },	/* PMC */
+	{ 1, 1, 1, 1, APIC_LVT_DM_NMI, 0 },	/* PMC */
 	{ 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_THERMAL_INT },	/* Thermal */
 };
 
@@ -307,11 +307,9 @@ lapic_setup(int boot)
 	lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0);
 	lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1);
 
-#ifdef	HWPMC_HOOKS
 	/* Program the PMC LVT entry if present. */
 	if (maxlvt >= LVT_PMC)
 		lapic->lvt_pcint = lvt_mode(la, LVT_PMC, lapic->lvt_pcint);
-#endif
 
 	/* Program timer LVT and setup handler. */
 	lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer);
@@ -334,6 +332,88 @@ lapic_setup(int boot)
 	intr_restore(eflags);
 }
 
+void
+lapic_reenable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	uint32_t value;
+
+	value =  lapic->lvt_pcint;
+	value &= ~APIC_LVT_M;
+	lapic->lvt_pcint = value;
+#endif
+}
+
+#ifdef HWPMC_HOOKS
+static void
+lapic_update_pmc(void *dummy)
+{
+	struct lapic *la;
+
+	la = &lapics[lapic_id()];
+	lapic->lvt_pcint = lvt_mode(la, LVT_PMC, lapic->lvt_pcint);
+}
+#endif
+
+int
+lapic_enable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	u_int32_t maxlvt;
+
+	/* Fail if the local APIC is not present. */
+	if (lapic == NULL)
+		return (0);
+
+	/* Fail if the PMC LVT is not present. */
+	maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
+	if (maxlvt < LVT_PMC)
+		return (0);
+
+	lvts[LVT_PMC].lvt_masked = 0;
+
+#ifdef SMP
+	/*
+	 * If hwpmc was loaded at boot time then the APs may not be
+	 * started yet.  In that case, don't forward the request to
+	 * them as they will program the lvt when they start.
+	 */
+	if (smp_started)
+		smp_rendezvous(NULL, lapic_update_pmc, NULL, NULL);
+	else
+#endif
+		lapic_update_pmc(NULL);
+	return (1);
+#else
+	return (0);
+#endif
+}
+
+void
+lapic_disable_pmc(void)
+{
+#ifdef HWPMC_HOOKS
+	u_int32_t maxlvt;
+
+	/* Fail if the local APIC is not present. */
+	if (lapic == NULL)
+		return;
+
+	/* Fail if the PMC LVT is not present. */
+	maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
+	if (maxlvt < LVT_PMC)
+		return;
+
+	lvts[LVT_PMC].lvt_masked = 1;
+
+#ifdef SMP
+	/* The APs should always be started when hwpmc is unloaded. */
+	KASSERT(mp_ncpus == 1 || smp_started, ("hwpmc unloaded too early"));
+#endif
+	smp_rendezvous(NULL, lapic_update_pmc, NULL, NULL);
+#endif
+}
+
 /*
  * Called by cpu_initclocks() on the BSP to setup the local APIC timer so
  * that it can drive hardclock, statclock, and profclock.  This function

Modified: stable/8/sys/i386/include/apicvar.h
==============================================================================
--- stable/8/sys/i386/include/apicvar.h	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/i386/include/apicvar.h	Fri Aug 14 20:57:21 2009	(r196223)
@@ -230,7 +230,9 @@ int	ioapic_set_triggermode(void *cookie,
 int	ioapic_set_smi(void *cookie, u_int pin);
 void	lapic_create(u_int apic_id, int boot_cpu);
 void	lapic_disable(void);
+void	lapic_disable_pmc(void);
 void	lapic_dump(const char *str);
+int	lapic_enable_pmc(void);
 void	lapic_eoi(void);
 u_int	lapic_error(void);
 int	lapic_id(void);
@@ -241,6 +243,7 @@ void	lapic_ipi_vectored(u_int vector, in
 int	lapic_ipi_wait(int delay);
 void	lapic_handle_intr(int vector, struct trapframe *frame);
 void	lapic_handle_timer(struct trapframe *frame);
+void	lapic_reenable_pmc(void);
 void	lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id);
 int	lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked);
 int	lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode);

Modified: stable/8/sys/i386/include/pmc_mdep.h
==============================================================================
--- stable/8/sys/i386/include/pmc_mdep.h	Fri Aug 14 20:42:40 2009	(r196222)
+++ stable/8/sys/i386/include/pmc_mdep.h	Fri Aug 14 20:57:21 2009	(r196223)
@@ -150,7 +150,6 @@ struct pmc_mdep;
  */
 
 void	start_exceptions(void), end_exceptions(void);
-void	pmc_x86_lapic_enable_pmc_interrupt(void);
 
 struct pmc_mdep *pmc_amd_initialize(void);
 void	pmc_amd_finalize(struct pmc_mdep *_md);


More information about the svn-src-stable mailing list