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:  alisa...@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)
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to