git: 04e832672159 - main - x86: Allow sharing of perfomance counter interrupts

From: Bojan Novković <bnovkov_at_FreeBSD.org>
Date: Sun, 15 Dec 2024 15:40:36 UTC
The branch main has been updated by bnovkov:

URL: https://cgit.FreeBSD.org/src/commit/?id=04e832672159cae412e8984e0b0cabfa6e7428b7

commit 04e832672159cae412e8984e0b0cabfa6e7428b7
Author:     Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2024-12-15 14:00:19 +0000
Commit:     Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2024-12-15 15:39:36 +0000

    x86: Allow sharing of perfomance counter interrupts
    
    This patch refactors the Performance Counter interrupt setup code to
    allow sharing the interrupt line between multiple drivers.
    More specifically, Performance Counter interrupts are used by both
    hwpmc(4) and hwt(4)'s upcoming Intel Processor Trace backend.
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D46420
---
 sys/dev/hwpmc/hwpmc_core.c |  4 ++--
 sys/dev/hwpmc/hwpmc_x86.c  |  4 ++--
 sys/x86/include/apicvar.h  |  6 +++---
 sys/x86/x86/local_apic.c   | 33 ++++++++++++++-------------------
 4 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/sys/dev/hwpmc/hwpmc_core.c b/sys/dev/hwpmc/hwpmc_core.c
index bf224ded126f..83784b93718e 100644
--- a/sys/dev/hwpmc/hwpmc_core.c
+++ b/sys/dev/hwpmc/hwpmc_core.c
@@ -1051,7 +1051,7 @@ core_intr(struct trapframe *tf)
 		counter_u64_add(pmc_stats.pm_intr_ignored, 1);
 
 	if (found_interrupt)
-		lapic_reenable_pmc();
+		lapic_reenable_pcint();
 
 	return (found_interrupt);
 }
@@ -1150,7 +1150,7 @@ core2_intr(struct trapframe *tf)
 		counter_u64_add(pmc_stats.pm_intr_ignored, 1);
 
 	if (found_interrupt)
-		lapic_reenable_pmc();
+		lapic_reenable_pcint();
 
 	/*
 	 * Reenable all non-stalled PMCs.
diff --git a/sys/dev/hwpmc/hwpmc_x86.c b/sys/dev/hwpmc/hwpmc_x86.c
index 1d04a6610674..54cc919eec30 100644
--- a/sys/dev/hwpmc/hwpmc_x86.c
+++ b/sys/dev/hwpmc/hwpmc_x86.c
@@ -242,7 +242,7 @@ pmc_md_initialize(void)
 		return (NULL);
 
 	/* disallow sampling if we do not have an LAPIC */
-	if (md != NULL && !lapic_enable_pmc())
+	if (md != NULL && !lapic_enable_pcint())
 		for (i = 0; i < md->pmd_nclass; i++) {
 			if (i == PMC_CLASS_INDEX_SOFT)
 				continue;
@@ -256,7 +256,7 @@ void
 pmc_md_finalize(struct pmc_mdep *md)
 {
 
-	lapic_disable_pmc();
+	lapic_disable_pcint();
 	if (cpu_vendor_id == CPU_VENDOR_AMD ||
 	    cpu_vendor_id == CPU_VENDOR_HYGON)
 		pmc_amd_finalize(md);
diff --git a/sys/x86/include/apicvar.h b/sys/x86/include/apicvar.h
index fc9bb0123539..c537d0ee0cdd 100644
--- a/sys/x86/include/apicvar.h
+++ b/sys/x86/include/apicvar.h
@@ -231,9 +231,9 @@ void	apic_enable_vector(u_int apic_id, u_int vector);
 void	apic_disable_vector(u_int apic_id, u_int vector);
 void	apic_free_vector(u_int apic_id, u_int vector, u_int irq);
 void	lapic_calibrate_timer(void);
-int	lapic_enable_pmc(void);
-void	lapic_disable_pmc(void);
-void	lapic_reenable_pmc(void);
+int	lapic_enable_pcint(void);
+void	lapic_disable_pcint(void);
+void	lapic_reenable_pcint(void);
 void	lapic_enable_cmc(void);
 int	lapic_enable_mca_elvt(void);
 void	lapic_ipi_raw(register_t icrlo, u_int dest);
diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c
index 6a913883cc5c..86cbe9a050dc 100644
--- a/sys/x86/x86/local_apic.c
+++ b/sys/x86/x86/local_apic.c
@@ -35,7 +35,6 @@
 
 #include <sys/cdefs.h>
 #include "opt_atpic.h"
-#include "opt_hwpmc_hooks.h"
 
 #include "opt_ddb.h"
 
@@ -50,6 +49,7 @@
 #include <sys/mutex.h>
 #include <sys/pcpu.h>
 #include <sys/proc.h>
+#include <sys/refcount.h>
 #include <sys/sched.h>
 #include <sys/smp.h>
 #include <sys/sysctl.h>
@@ -206,6 +206,7 @@ static uint64_t lapic_ipi_wait_mult;
 static int __read_mostly lapic_ds_idle_timeout = 1000000;
 #endif
 unsigned int max_apic_id;
+static int pcint_refcnt = 0;
 
 SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
     "APIC options");
@@ -809,20 +810,19 @@ lapic_intrcnt(void *dummy __unused)
 SYSINIT(lapic_intrcnt, SI_SUB_INTR, SI_ORDER_MIDDLE, lapic_intrcnt, NULL);
 
 void
-lapic_reenable_pmc(void)
+lapic_reenable_pcint(void)
 {
-#ifdef HWPMC_HOOKS
 	uint32_t value;
 
+	if (refcount_load(&pcint_refcnt) == 0)
+		return;
 	value = lapic_read32(LAPIC_LVT_PCINT);
 	value &= ~APIC_LVT_M;
 	lapic_write32(LAPIC_LVT_PCINT, value);
-#endif
 }
 
-#ifdef HWPMC_HOOKS
 static void
-lapic_update_pmc(void *dummy)
+lapic_update_pcint(void *dummy)
 {
 	struct lapic *la;
 
@@ -830,7 +830,6 @@ lapic_update_pmc(void *dummy)
 	lapic_write32(LAPIC_LVT_PCINT, lvt_mode(la, APIC_LVT_PMC,
 	    lapic_read32(LAPIC_LVT_PCINT)));
 }
-#endif
 
 void
 lapic_calibrate_timer(void)
@@ -858,9 +857,8 @@ lapic_calibrate_timer(void)
 }
 
 int
-lapic_enable_pmc(void)
+lapic_enable_pcint(void)
 {
-#ifdef HWPMC_HOOKS
 	u_int32_t maxlvt;
 
 #ifdef DEV_ATPIC
@@ -873,21 +871,18 @@ lapic_enable_pmc(void)
 	maxlvt = (lapic_read32(LAPIC_VERSION) & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
 	if (maxlvt < APIC_LVT_PMC)
 		return (0);
-
+	if (refcount_acquire(&pcint_refcnt) > 0)
+		return (1);
 	lvts[APIC_LVT_PMC].lvt_masked = 0;
 
 	MPASS(mp_ncpus == 1 || smp_started);
-	smp_rendezvous(NULL, lapic_update_pmc, NULL, NULL);
+	smp_rendezvous(NULL, lapic_update_pcint, NULL, NULL);
 	return (1);
-#else
-	return (0);
-#endif
 }
 
 void
-lapic_disable_pmc(void)
+lapic_disable_pcint(void)
 {
-#ifdef HWPMC_HOOKS
 	u_int32_t maxlvt;
 
 #ifdef DEV_ATPIC
@@ -900,15 +895,15 @@ lapic_disable_pmc(void)
 	maxlvt = (lapic_read32(LAPIC_VERSION) & APIC_VER_MAXLVT) >> MAXLVTSHIFT;
 	if (maxlvt < APIC_LVT_PMC)
 		return;
-
+	if (refcount_release(&pcint_refcnt))
+		return;
 	lvts[APIC_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
+	smp_rendezvous(NULL, lapic_update_pcint, NULL, NULL);
 }
 
 static int