Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 May 2020 15:27:20 +0000 (UTC)
From:      Andrew Turner <andrew@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r361258 - head/sys/arm64/arm64
Message-ID:  <202005191527.04JFRKjG077740@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202005191527.04JFRKjG077740>