svn commit: r361258 - head/sys/arm64/arm64
Andrew Turner
andrew at FreeBSD.org
Tue May 19 15:27:21 UTC 2020
Author: andrew
Date: Tue May 19 15:27:20 2020
New Revision: 361258
URL: https://svnweb.freebsd.org/changeset/base/361258
Log:
Create MSI/MSI-X isrcs as needed in the GICv3 ITS driver
Previously we would create an isrc for each MSI/MSI-X interrupt. This
causes issues for other interrupt sources in the system, e.g. a GPIO
driver, as they may be unable to allocate interrupts. This works around
this by allocating the isrc only when needed.
Reported by: alisaidi at amazon.com
Reviewed by: mmel
Sponsored by: Innovaate UK
Differential Revision: https://reviews.freebsd.org/D24876
Modified:
head/sys/arm64/arm64/gicv3_its.c
Modified: head/sys/arm64/arm64/gicv3_its.c
==============================================================================
--- head/sys/arm64/arm64/gicv3_its.c Tue May 19 15:19:39 2020 (r361257)
+++ head/sys/arm64/arm64/gicv3_its.c Tue May 19 15:27:20 2020 (r361258)
@@ -228,6 +228,7 @@ struct gicv3_its_irqsrc {
u_int gi_id;
u_int gi_lpi;
struct its_dev *gi_its_dev;
+ TAILQ_ENTRY(gicv3_its_irqsrc) gi_link;
};
struct gicv3_its_softc {
@@ -254,12 +255,14 @@ struct gicv3_its_softc {
size_t sc_its_cmd_next_idx;
vmem_t *sc_irq_alloc;
- struct gicv3_its_irqsrc *sc_irqs;
+ struct gicv3_its_irqsrc **sc_irqs;
u_int sc_irq_base;
u_int sc_irq_length;
+ u_int sc_irq_count;
struct mtx sc_its_dev_lock;
TAILQ_HEAD(its_dev_list, its_dev) sc_its_dev_list;
+ TAILQ_HEAD(free_irqs, gicv3_its_irqsrc) sc_free_irqs;
#define ITS_FLAGS_CMDQ_FLUSH 0x00000001
#define ITS_FLAGS_LPI_CONF_FLUSH 0x00000002
@@ -800,7 +803,6 @@ static int
gicv3_its_attach(device_t dev)
{
struct gicv3_its_softc *sc;
- const char *name;
uint32_t iidr;
int domain, err, i, rid;
@@ -875,6 +877,7 @@ gicv3_its_attach(device_t dev)
its_init_cpu(dev, sc);
TAILQ_INIT(&sc->sc_its_dev_list);
+ TAILQ_INIT(&sc->sc_free_irqs);
/*
* Create the vmem object to allocate INTRNG IRQs from. We try to
@@ -887,13 +890,6 @@ gicv3_its_attach(device_t dev)
sc->sc_irqs = malloc(sizeof(*sc->sc_irqs) * sc->sc_irq_length,
M_GICV3_ITS, M_WAITOK | M_ZERO);
- name = device_get_nameunit(dev);
- for (i = 0; i < sc->sc_irq_length; i++) {
- sc->sc_irqs[i].gi_id = -1;
- sc->sc_irqs[i].gi_lpi = i + sc->sc_irq_base - GIC_FIRST_LPI;
- err = intr_isrc_register(&sc->sc_irqs[i].gi_isrc, dev, 0,
- "%s,%u", name, i);
- }
/* For GIC-500 install tracking sysctls. */
if ((iidr & (GITS_IIDR_PRODUCT_MASK | GITS_IIDR_IMPLEMENTOR_MASK)) ==
@@ -975,7 +971,7 @@ gicv3_its_intr(void *arg, uintptr_t irq)
struct trapframe *tf;
irq -= sc->sc_irq_base;
- girq = &sc->sc_irqs[irq];
+ girq = sc->sc_irqs[irq];
if (girq == NULL)
panic("gicv3_its_intr: Invalid interrupt %ld",
irq + sc->sc_irq_base);
@@ -1201,6 +1197,53 @@ its_device_release(device_t dev, struct its_dev *its_d
free(its_dev, M_GICV3_ITS);
}
+static struct gicv3_its_irqsrc *
+gicv3_its_alloc_irqsrc(device_t dev, struct gicv3_its_softc *sc, u_int irq)
+{
+ struct gicv3_its_irqsrc *girq = NULL;
+
+ KASSERT(sc->sc_irqs[irq] == NULL,
+ ("%s: Interrupt %u already allocated", __func__, irq));
+ mtx_lock_spin(&sc->sc_its_dev_lock);
+ if (!TAILQ_EMPTY(&sc->sc_free_irqs)) {
+ girq = TAILQ_FIRST(&sc->sc_free_irqs);
+ TAILQ_REMOVE(&sc->sc_free_irqs, girq, gi_link);
+ }
+ mtx_unlock_spin(&sc->sc_its_dev_lock);
+ if (girq == NULL) {
+ girq = malloc(sizeof(*girq), M_GICV3_ITS,
+ M_NOWAIT | M_ZERO);
+ if (girq == NULL)
+ return (NULL);
+ girq->gi_id = -1;
+ if (intr_isrc_register(&girq->gi_isrc, dev, 0,
+ "%s,%u", device_get_nameunit(dev), irq) != 0) {
+ free(girq, M_GICV3_ITS);
+ return (NULL);
+ }
+ }
+ girq->gi_lpi = irq + sc->sc_irq_base - GIC_FIRST_LPI;
+ sc->sc_irqs[irq] = girq;
+
+ return (girq);
+}
+
+static void
+gicv3_its_release_irqsrc(struct gicv3_its_softc *sc,
+ struct gicv3_its_irqsrc *girq)
+{
+ u_int irq;
+
+ mtx_assert(&sc->sc_its_dev_lock, MA_OWNED);
+
+ irq = girq->gi_lpi + GIC_FIRST_LPI - sc->sc_irq_base;
+ sc->sc_irqs[irq] = NULL;
+
+ girq->gi_id = -1;
+ girq->gi_its_dev = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_free_irqs, girq, gi_link);
+}
+
static int
gicv3_its_alloc_msi(device_t dev, device_t child, int count, int maxcount,
device_t *pic, struct intr_irqsrc **srcs)
@@ -1220,12 +1263,35 @@ gicv3_its_alloc_msi(device_t dev, device_t child, int
sc = device_get_softc(dev);
irq = its_dev->lpis.lpi_base + its_dev->lpis.lpi_num -
its_dev->lpis.lpi_free;
+
+ /* Allocate the irqsrc for each MSI */
for (i = 0; i < count; i++, irq++) {
its_dev->lpis.lpi_free--;
- girq = &sc->sc_irqs[irq];
+ srcs[i] = (struct intr_irqsrc *)gicv3_its_alloc_irqsrc(dev,
+ sc, irq);
+ if (srcs[i] == NULL)
+ break;
+ }
+
+ /* The allocation failed, release them */
+ if (i != count) {
+ mtx_lock_spin(&sc->sc_its_dev_lock);
+ for (i = 0; i < count; i++) {
+ girq = (struct gicv3_its_irqsrc *)srcs[i];
+ if (girq == NULL)
+ break;
+ gicv3_its_release_irqsrc(sc, girq);
+ srcs[i] = NULL;
+ }
+ mtx_unlock_spin(&sc->sc_its_dev_lock);
+ return (ENXIO);
+ }
+
+ /* Finish the allocation now we have all MSI irqsrcs */
+ for (i = 0; i < count; i++) {
+ girq = (struct gicv3_its_irqsrc *)srcs[i];
girq->gi_id = i;
girq->gi_its_dev = its_dev;
- srcs[i] = (struct intr_irqsrc *)girq;
/* Map the message to the given IRQ */
gicv3_its_select_cpu(dev, (struct intr_irqsrc *)girq);
@@ -1241,6 +1307,7 @@ static int
gicv3_its_release_msi(device_t dev, device_t child, int count,
struct intr_irqsrc **isrc)
{
+ struct gicv3_its_softc *sc;
struct gicv3_its_irqsrc *girq;
struct its_dev *its_dev;
int i;
@@ -1254,11 +1321,14 @@ gicv3_its_release_msi(device_t dev, device_t child, in
("gicv3_its_release_msi: Releasing more interrupts than "
"were allocated: releasing %d, allocated %d", count,
its_dev->lpis.lpi_busy));
+
+ sc = device_get_softc(dev);
+ mtx_lock_spin(&sc->sc_its_dev_lock);
for (i = 0; i < count; i++) {
girq = (struct gicv3_its_irqsrc *)isrc[i];
- girq->gi_id = -1;
- girq->gi_its_dev = NULL;
+ gicv3_its_release_irqsrc(sc, girq);
}
+ mtx_unlock_spin(&sc->sc_its_dev_lock);
its_dev->lpis.lpi_busy -= count;
if (its_dev->lpis.lpi_busy == 0)
@@ -1286,7 +1356,10 @@ gicv3_its_alloc_msix(device_t dev, device_t child, dev
sc = device_get_softc(dev);
irq = its_dev->lpis.lpi_base + its_dev->lpis.lpi_num -
its_dev->lpis.lpi_free;
- girq = &sc->sc_irqs[irq];
+
+ girq = gicv3_its_alloc_irqsrc(dev, sc, irq);
+ if (girq == NULL)
+ return (ENXIO);
girq->gi_id = its_dev->lpis.lpi_busy;
girq->gi_its_dev = its_dev;
@@ -1306,6 +1379,7 @@ gicv3_its_alloc_msix(device_t dev, device_t child, dev
static int
gicv3_its_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
{
+ struct gicv3_its_softc *sc;
struct gicv3_its_irqsrc *girq;
struct its_dev *its_dev;
@@ -1317,9 +1391,10 @@ gicv3_its_release_msix(device_t dev, device_t child, s
KASSERT(its_dev->lpis.lpi_busy > 0,
("gicv3_its_release_msix: Releasing more interrupts than "
"were allocated: allocated %d", its_dev->lpis.lpi_busy));
+
+ sc = device_get_softc(dev);
girq = (struct gicv3_its_irqsrc *)isrc;
- girq->gi_its_dev = NULL;
- girq->gi_id = -1;
+ gicv3_its_release_irqsrc(sc, girq);
its_dev->lpis.lpi_busy--;
if (its_dev->lpis.lpi_busy == 0)
More information about the svn-src-all
mailing list