summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzbb <zbb@FreeBSD.org>2016-05-18 10:09:07 +0000
committerzbb <zbb@FreeBSD.org>2016-05-18 10:09:07 +0000
commit6fac2e64afa4d919233ab8b0aa6271dae818e86a (patch)
tree1eac776b2ec09f7dce09142b4f9e7ab3f4ad27a4
parent921cbe3305f451d2a538e0dc8e078c53660274ff (diff)
downloadFreeBSD-src-6fac2e64afa4d919233ab8b0aa6271dae818e86a.zip
FreeBSD-src-6fac2e64afa4d919233ab8b0aa6271dae818e86a.tar.gz
Add support for MSI/MSIX deallocation on GICv3-ITS
Allow to deallocate previously allocated ITS device along with its interrupts. Interrupt numbers are being freed when the last LPI number is no longer busy. Reviewed by: wma Obtained from: Semihalf Sponsored by: Cavium Differential Revision: https://reviews.freebsd.org/D6351
-rw-r--r--sys/arm64/arm64/gic_v3_its.c88
-rw-r--r--sys/arm64/arm64/gic_v3_var.h7
2 files changed, 87 insertions, 8 deletions
diff --git a/sys/arm64/arm64/gic_v3_its.c b/sys/arm64/arm64/gic_v3_its.c
index a2cfa55..c16cf0a 100644
--- a/sys/arm64/arm64/gic_v3_its.c
+++ b/sys/arm64/arm64/gic_v3_its.c
@@ -75,8 +75,10 @@ static device_method_t gic_v3_its_methods[] = {
*/
/* MSI-X */
DEVMETHOD(pic_alloc_msix, gic_v3_its_alloc_msix),
+ DEVMETHOD(pic_release_msix, gic_v3_its_release_msix),
/* MSI */
DEVMETHOD(pic_alloc_msi, gic_v3_its_alloc_msi),
+ DEVMETHOD(pic_release_msi, gic_v3_its_release_msi),
DEVMETHOD(pic_map_msi, gic_v3_its_map_msi),
/* End */
@@ -882,6 +884,7 @@ retry:
bit_nset(bitmap, fclr, fclr + nvecs - 1);
lpic->lpi_base = fclr + GIC_FIRST_LPI;
lpic->lpi_num = nvecs;
+ lpic->lpi_busy = 0;
lpic->lpi_free = lpic->lpi_num;
lpic->lpi_col_ids = col_ids;
for (i = 0; i < lpic->lpi_num; i++) {
@@ -901,10 +904,9 @@ lpi_free_chunk(struct gic_v3_its_softc *sc, struct lpi_chunk *lpic)
{
int start, end;
- KASSERT((lpic->lpi_free == lpic->lpi_num),
- ("Trying to free LPI chunk that is still in use.\n"));
-
mtx_lock_spin(&sc->its_dev_lock);
+ KASSERT((lpic->lpi_busy == 0),
+ ("Trying to free LPI chunk that is still in use.\n"));
/* First bit of this chunk in a global bitmap */
start = lpic->lpi_base - GIC_FIRST_LPI;
/* and last bit of this chunk... */
@@ -1493,6 +1495,7 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
u_int nvecs)
{
struct its_dev *newdev;
+ vm_offset_t itt_addr;
uint64_t typer;
uint32_t devid;
size_t esize;
@@ -1528,16 +1531,18 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
* Allocate ITT for this device.
* PA has to be 256 B aligned. At least two entries for device.
*/
- newdev->itt = (vm_offset_t)contigmalloc(
- roundup2(roundup2(nvecs, 2) * esize, 0x100), M_GIC_V3_ITS,
- (M_NOWAIT | M_ZERO), 0, ~0UL, 0x100, 0);
- if (newdev->itt == 0) {
+ newdev->itt_size = roundup2(roundup2(nvecs, 2) * esize, 0x100);
+ itt_addr = (vm_offset_t)contigmalloc(
+ newdev->itt_size, M_GIC_V3_ITS, (M_NOWAIT | M_ZERO),
+ 0, ~0UL, 0x100, 0);
+ if (itt_addr == 0) {
lpi_free_chunk(sc, &newdev->lpis);
free(newdev, M_GIC_V3_ITS);
return (NULL);
}
mtx_lock_spin(&sc->its_dev_lock);
+ newdev->itt = itt_addr;
TAILQ_INSERT_TAIL(&sc->its_dev_list, newdev, entry);
mtx_unlock_spin(&sc->its_dev_lock);
@@ -1547,6 +1552,50 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
return (newdev);
}
+static void
+its_device_free(struct gic_v3_its_softc *sc, device_t pci_dev,
+ u_int nvecs)
+{
+ struct its_dev *odev;
+
+ mtx_lock_spin(&sc->its_dev_lock);
+ /* Find existing device if any */
+ odev = its_device_find_locked(sc, pci_dev, 0);
+ if (odev == NULL) {
+ mtx_unlock_spin(&sc->its_dev_lock);
+ return;
+ }
+
+ KASSERT((nvecs <= odev->lpis.lpi_num) && (nvecs <= odev->lpis.lpi_busy),
+ ("Invalid number of LPI vectors to free %d (total %d) (busy %d)",
+ nvecs, odev->lpis.lpi_num, odev->lpis.lpi_busy));
+ /* Just decrement number of busy LPIs in chunk */
+ odev->lpis.lpi_busy -= nvecs;
+ if (odev->lpis.lpi_busy != 0) {
+ mtx_unlock_spin(&sc->its_dev_lock);
+ return;
+ }
+
+ /*
+ * At that point we know that there are no busy LPIs for this device.
+ * Entire ITS device can now be removed.
+ */
+ mtx_unlock_spin(&sc->its_dev_lock);
+ /* Unmap device in ITS */
+ its_cmd_mapd(sc, odev, 0);
+ /* Free ITT */
+ KASSERT(odev->itt != 0, ("Invalid ITT in valid ITS device"));
+ contigfree((void *)odev->itt, odev->itt_size, M_GIC_V3_ITS);
+ /* Free chunk */
+ lpi_free_chunk(sc, &odev->lpis);
+ /* Free device */
+ mtx_lock_spin(&sc->its_dev_lock);
+ TAILQ_REMOVE(&sc->its_dev_list, odev, entry);
+ mtx_unlock_spin(&sc->its_dev_lock);
+ free((void *)odev, M_GIC_V3_ITS);
+
+}
+
static __inline void
its_device_asign_lpi_locked(struct gic_v3_its_softc *sc,
struct its_dev *its_dev, u_int *irq)
@@ -1561,6 +1610,7 @@ its_device_asign_lpi_locked(struct gic_v3_its_softc *sc,
*irq = its_dev->lpis.lpi_base + (its_dev->lpis.lpi_num -
its_dev->lpis.lpi_free);
its_dev->lpis.lpi_free--;
+ its_dev->lpis.lpi_busy++;
}
/*
@@ -1678,6 +1728,18 @@ gic_v3_its_alloc_msix(device_t dev, device_t pci_dev, int *irq)
}
int
+gic_v3_its_release_msix(device_t dev, device_t pci_dev, int irq __unused)
+{
+
+ struct gic_v3_its_softc *sc;
+
+ sc = device_get_softc(dev);
+ its_device_free(sc, pci_dev, 1);
+
+ return (0);
+}
+
+int
gic_v3_its_alloc_msi(device_t dev, device_t pci_dev, int count, int *irqs)
{
struct gic_v3_its_softc *sc;
@@ -1701,6 +1763,18 @@ gic_v3_its_alloc_msi(device_t dev, device_t pci_dev, int count, int *irqs)
}
int
+gic_v3_its_release_msi(device_t dev, device_t pci_dev, int count,
+ int *irqs __unused)
+{
+ struct gic_v3_its_softc *sc;
+
+ sc = device_get_softc(dev);
+ its_device_free(sc, pci_dev, count);
+
+ return (0);
+}
+
+int
gic_v3_its_map_msi(device_t dev, device_t pci_dev, int irq, uint64_t *addr,
uint32_t *data)
{
diff --git a/sys/arm64/arm64/gic_v3_var.h b/sys/arm64/arm64/gic_v3_var.h
index 6283e4e..5d026b9 100644
--- a/sys/arm64/arm64/gic_v3_var.h
+++ b/sys/arm64/arm64/gic_v3_var.h
@@ -112,9 +112,11 @@ DECLARE_CLASS(gic_v3_its_driver);
/* LPI chunk owned by ITS device */
struct lpi_chunk {
u_int lpi_base;
- u_int lpi_num;
u_int lpi_free; /* First free LPI in set */
u_int *lpi_col_ids;
+
+ u_int lpi_num; /* Total number of LPIs in chunk */
+ u_int lpi_busy; /* Number of busy LPIs in chink */
};
/* ITS device */
@@ -128,6 +130,7 @@ struct its_dev {
struct lpi_chunk lpis;
/* Virtual address of ITT */
vm_offset_t itt;
+ size_t itt_size;
};
TAILQ_HEAD(its_dev_list, its_dev);
@@ -277,7 +280,9 @@ extern devclass_t gic_v3_its_devclass;
int gic_v3_its_detach(device_t);
int gic_v3_its_alloc_msix(device_t, device_t, int *);
+int gic_v3_its_release_msix(device_t, device_t, int);
int gic_v3_its_alloc_msi(device_t, device_t, int, int *);
+int gic_v3_its_release_msi(device_t, device_t, int, int *);
int gic_v3_its_map_msi(device_t, device_t, int, uint64_t *, uint32_t *);
int its_init_cpu(struct gic_v3_its_softc *);
OpenPOWER on IntegriCloud