git: 7604062517ec - stable/14 - intrng: Allow alternative IPI PICs to be registered and used

From: Jessica Clarke <jrtc27_at_FreeBSD.org>
Date: Sat, 07 Sep 2024 01:47:54 UTC
The branch stable/14 has been updated by jrtc27:

URL: https://cgit.FreeBSD.org/src/commit/?id=7604062517ec25ac0412f258b36f58774bd234ce

commit 7604062517ec25ac0412f258b36f58774bd234ce
Author:     Jessica Clarke <jrtc27@FreeBSD.org>
AuthorDate: 2024-01-24 23:49:54 +0000
Commit:     Jessica Clarke <jrtc27@FreeBSD.org>
CommitDate: 2024-09-06 23:59:02 +0000

    intrng: Allow alternative IPI PICs to be registered and used
    
    On RISC-V, the root PIC (whether the PLIC or, as will be the case in
    future, the local interrupt controller) cannot send IPIs, relying on
    another means to trigger the necessary software interrupts (firmware
    calls), but there are upcoming standard devices that will be able to
    inject them, so we can't just put the firmware calls in the root PIC
    driver.
    
    Thus, split out a new intr_ipi_dev from intr_irq_root_dev to use for
    sending IPIs. New devices can be registered with a given priority up
    until the first IPI is set up, when the best device seen so far gets
    frozen as the IPI device to use.
    
    Reviewed by:    mhorne
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D35899
    
    (cherry picked from commit 103d39efe0c68cb2a808c306b14c3f473a02535d)
---
 sys/arm/arm/gic_acpi.c             |  8 ++++++++
 sys/arm/arm/gic_fdt.c              |  7 +++++++
 sys/arm/broadcom/bcm2835/bcm2836.c | 12 ++++++++++-
 sys/arm64/arm64/gic_v3_acpi.c      |  8 ++++++++
 sys/arm64/arm64/gic_v3_fdt.c       |  8 ++++++++
 sys/kern/subr_intr.c               | 42 +++++++++++++++++++++++++++++++-------
 sys/sys/intr.h                     |  1 +
 7 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/sys/arm/arm/gic_acpi.c b/sys/arm/arm/gic_acpi.c
index 67297f9879ca..bc03bf742798 100644
--- a/sys/arm/arm/gic_acpi.c
+++ b/sys/arm/arm/gic_acpi.c
@@ -233,6 +233,14 @@ gic_acpi_attach(device_t dev)
 		intr_pic_deregister(dev, xref);
 		goto cleanup;
 	}
+
+#ifdef SMP
+	if (intr_ipi_pic_register(dev, 0) != 0) {
+		device_printf(dev, "could not register for IPIs\n");
+		goto cleanup;
+	}
+#endif
+
 	/* If we have children probe and attach them */
 	if (arm_gic_add_children(dev)) {
 		bus_generic_probe(dev);
diff --git a/sys/arm/arm/gic_fdt.c b/sys/arm/arm/gic_fdt.c
index e7fe503bf426..4a83610d3418 100644
--- a/sys/arm/arm/gic_fdt.c
+++ b/sys/arm/arm/gic_fdt.c
@@ -159,6 +159,13 @@ gic_fdt_attach(device_t dev)
 			intr_pic_deregister(dev, xref);
 			goto cleanup;
 		}
+
+#ifdef SMP
+		if (intr_ipi_pic_register(dev, 0) != 0) {
+			device_printf(dev, "could not register for IPIs\n");
+			goto cleanup;
+		}
+#endif
 	} else {
 		if (sc->base.gic_res[2] == NULL) {
 			device_printf(dev,
diff --git a/sys/arm/broadcom/bcm2835/bcm2836.c b/sys/arm/broadcom/bcm2835/bcm2836.c
index f72bd9075982..8ff824d34521 100644
--- a/sys/arm/broadcom/bcm2835/bcm2836.c
+++ b/sys/arm/broadcom/bcm2835/bcm2836.c
@@ -646,7 +646,17 @@ bcm_lintc_pic_attach(struct bcm_lintc_softc *sc)
 	if (pic == NULL)
 		return (ENXIO);
 
-	return (intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc));
+	error = intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc);
+	if (error != 0)
+		return (error);
+
+#ifdef SMP
+	error = intr_ipi_pic_register(sc->bls_dev, 0);
+	if (error != 0)
+		return (error);
+#endif
+
+	return (0);
 }
 
 static int
diff --git a/sys/arm64/arm64/gic_v3_acpi.c b/sys/arm64/arm64/gic_v3_acpi.c
index 199fddcbc20c..fca73010759d 100644
--- a/sys/arm64/arm64/gic_v3_acpi.c
+++ b/sys/arm64/arm64/gic_v3_acpi.c
@@ -352,6 +352,14 @@ gic_v3_acpi_attach(device_t dev)
 		goto error;
 	}
 
+#ifdef SMP
+	err = intr_ipi_pic_register(dev, 0);
+	if (err != 0) {
+		device_printf(dev, "could not register for IPIs\n");
+		goto error;
+	}
+#endif
+
 	/*
 	 * Try to register the ITS driver to this GIC. The GIC will act as
 	 * a bus in that case. Failure here will not affect the main GIC
diff --git a/sys/arm64/arm64/gic_v3_fdt.c b/sys/arm64/arm64/gic_v3_fdt.c
index f094bc6eb1fe..ad27e395ad98 100644
--- a/sys/arm64/arm64/gic_v3_fdt.c
+++ b/sys/arm64/arm64/gic_v3_fdt.c
@@ -167,6 +167,14 @@ gic_v3_fdt_attach(device_t dev)
 		goto error;
 	}
 
+#ifdef SMP
+	err = intr_ipi_pic_register(dev, 0);
+	if (err != 0) {
+		device_printf(dev, "could not register for IPIs\n");
+		goto error;
+	}
+#endif
+
 	/*
 	 * Try to register ITS to this GIC.
 	 * GIC will act as a bus in that case.
diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c
index b9022f44b880..6198603adfe8 100644
--- a/sys/kern/subr_intr.c
+++ b/sys/kern/subr_intr.c
@@ -139,6 +139,10 @@ struct intr_ipi {
 	char			ii_name[INTR_IPI_NAMELEN];
 	u_long			*ii_count;
 };
+
+static device_t intr_ipi_dev;
+static u_int intr_ipi_dev_priority;
+static bool intr_ipi_dev_frozen;
 #endif
 
 static struct mtx pic_list_lock;
@@ -380,7 +384,8 @@ intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
 
 	KASSERT(isrc != NULL, ("%s: no source", __func__));
 
-	isrc_increment_count(isrc);
+	if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
+		isrc_increment_count(isrc);
 
 #ifdef INTR_SOLO
 	if (isrc->isrc_filter != NULL) {
@@ -396,7 +401,8 @@ intr_isrc_dispatch(struct intr_irqsrc *isrc, struct trapframe *tf)
 			return (0);
 	}
 
-	isrc_increment_straycount(isrc);
+	if ((isrc->isrc_flags & INTR_ISRCF_IPI) == 0)
+		isrc_increment_straycount(isrc);
 	return (EINVAL);
 }
 
@@ -1815,6 +1821,20 @@ intr_ipi_lookup(u_int ipi)
 	return (&ipi_sources[ipi]);
 }
 
+int
+intr_ipi_pic_register(device_t dev, u_int priority)
+{
+	if (intr_ipi_dev_frozen) {
+		device_printf(dev, "IPI device already frozen");
+		return (EBUSY);
+	}
+
+	if (intr_ipi_dev == NULL || priority > intr_ipi_dev_priority)
+		intr_ipi_dev = dev;
+
+	return (0);
+}
+
 /*
  *  Setup IPI handler on interrupt controller.
  *
@@ -1828,10 +1848,17 @@ intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
 	struct intr_ipi *ii;
 	int error;
 
-	KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
+	if (!intr_ipi_dev_frozen) {
+		if (intr_ipi_dev == NULL)
+			panic("%s: no IPI PIC attached", __func__);
+
+		intr_ipi_dev_frozen = true;
+		device_printf(intr_ipi_dev, "using for IPIs\n");
+	}
+
 	KASSERT(hand != NULL, ("%s: ipi %u no handler", __func__, ipi));
 
-	error = PIC_IPI_SETUP(intr_irq_root_dev, ipi, &isrc);
+	error = PIC_IPI_SETUP(intr_ipi_dev, ipi, &isrc);
 	if (error != 0)
 		return;
 
@@ -1846,7 +1873,7 @@ intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
 	strlcpy(ii->ii_name, name, INTR_IPI_NAMELEN);
 	ii->ii_count = intr_ipi_setup_counters(name);
 
-	PIC_ENABLE_INTR(intr_irq_root_dev, isrc);
+	PIC_ENABLE_INTR(intr_ipi_dev, isrc);
 }
 
 void
@@ -1854,7 +1881,8 @@ intr_ipi_send(cpuset_t cpus, u_int ipi)
 {
 	struct intr_ipi *ii;
 
-	KASSERT(intr_irq_root_dev != NULL, ("%s: no root attached", __func__));
+	KASSERT(intr_ipi_dev_frozen,
+	    ("%s: IPI device not yet frozen", __func__));
 
 	ii = intr_ipi_lookup(ipi);
 	if (ii->ii_count == NULL)
@@ -1873,7 +1901,7 @@ intr_ipi_send(cpuset_t cpus, u_int ipi)
 	dsb(ishst);
 #endif
 
-	PIC_IPI_SEND(intr_irq_root_dev, ii->ii_isrc, cpus, ipi);
+	PIC_IPI_SEND(intr_ipi_dev, ii->ii_isrc, cpus, ipi);
 }
 
 /*
diff --git a/sys/sys/intr.h b/sys/sys/intr.h
index 57b0ca393912..f6957864c639 100644
--- a/sys/sys/intr.h
+++ b/sys/sys/intr.h
@@ -156,6 +156,7 @@ extern u_int	intr_nirq;	/* number of IRQs on intrng platforms */
 #ifdef SMP
 typedef void intr_ipi_handler_t(void *);
 
+int intr_ipi_pic_register(device_t dev, u_int priority);
 void intr_ipi_setup(u_int ipi, const char *name, intr_ipi_handler_t *hand,
     void *arg);
 void intr_ipi_send(cpuset_t cpus, u_int ipi);