Support interrupts binding in GICv3 and ITS

- Add MOVI command and routine for the LPI migration
- Allow to search for the ITS device descriptor using
  not only devID but also LPI number.
- Bind SPIs in the Distributor
- Don't bind its_dev to collection. Keep track of the collection
  IDs for each LPI.

Reviewed by:   wma
Obtained from: Semihalf
Sponsored by:  Cavium
Differential Revision: https://reviews.freebsd.org/D5231
This commit is contained in:
Zbigniew Bodek 2016-02-11 12:04:58 +00:00
parent 907a0579aa
commit 6cd36342c0
3 changed files with 167 additions and 35 deletions

View File

@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
#include "gic_v3_var.h"
/* Device and PIC methods */
static int gic_v3_bind(device_t, u_int, u_int);
static void gic_v3_dispatch(device_t, struct trapframe *);
static void gic_v3_eoi(device_t, u_int);
static void gic_v3_mask_irq(device_t, u_int);
@ -72,6 +73,7 @@ static device_method_t gic_v3_methods[] = {
DEVMETHOD(device_detach, gic_v3_detach),
/* PIC interface */
DEVMETHOD(pic_bind, gic_v3_bind),
DEVMETHOD(pic_dispatch, gic_v3_dispatch),
DEVMETHOD(pic_eoi, gic_v3_eoi),
DEVMETHOD(pic_mask, gic_v3_mask_irq),
@ -244,6 +246,28 @@ gic_v3_detach(device_t dev)
/*
* PIC interface.
*/
static int
gic_v3_bind(device_t dev, u_int irq, u_int cpuid)
{
uint64_t aff;
struct gic_v3_softc *sc;
sc = device_get_softc(dev);
if (irq <= GIC_LAST_PPI) {
/* Can't bind PPI to another CPU but it's not an error */
return (0);
} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) {
aff = CPU_AFFINITY(cpuid);
gic_d_write(sc, 4, GICD_IROUTER(irq), aff);
return (0);
} else if (irq >= GIC_FIRST_LPI)
return (lpi_migrate(dev, irq, cpuid));
return (EINVAL);
}
static void
gic_v3_dispatch(device_t dev, struct trapframe *frame)
{

View File

@ -92,9 +92,13 @@ static void its_free_tables(struct gic_v3_its_softc *);
static void its_init_commandq(struct gic_v3_its_softc *);
static void its_init_cpu_collection(struct gic_v3_its_softc *);
static uint32_t its_get_devid(device_t);
static struct its_dev * its_device_find_locked(struct gic_v3_its_softc *,
device_t, uint32_t);
static int its_cmd_send(struct gic_v3_its_softc *, struct its_cmd_desc *);
static void its_cmd_movi(struct gic_v3_its_softc *, struct its_dev *,
struct its_col *, uint32_t);
static void its_cmd_mapc(struct gic_v3_its_softc *, struct its_col *, uint8_t);
static void its_cmd_mapvi(struct gic_v3_its_softc *, struct its_dev *, uint32_t,
uint32_t);
@ -846,18 +850,28 @@ static int
lpi_alloc_chunk(struct gic_v3_its_softc *sc, struct lpi_chunk *lpic,
u_int nvecs)
{
u_int *col_ids;
int fclr; /* First cleared bit */
uint8_t *bitmap;
size_t nb, i;
col_ids = malloc(sizeof(*col_ids) * nvecs, M_GIC_V3_ITS,
(M_NOWAIT | M_ZERO));
if (col_ids == NULL)
return (ENOMEM);
mtx_lock_spin(&sc->its_dev_lock);
bitmap = (uint8_t *)sc->its_lpi_bitmap;
fclr = 0;
retry:
/* Check other bits - sloooow */
for (i = 0, nb = fclr; i < nvecs; i++, nb++) {
if (nb > sc->its_lpi_maxid)
if (nb > sc->its_lpi_maxid) {
mtx_unlock_spin(&sc->its_dev_lock);
free(col_ids, M_GIC_V3_ITS);
return (EINVAL);
}
if (isset(bitmap, nb)) {
/* To little free bits in this area. Move on. */
@ -870,6 +884,15 @@ lpi_alloc_chunk(struct gic_v3_its_softc *sc, struct lpi_chunk *lpic,
lpic->lpi_base = fclr + GIC_FIRST_LPI;
lpic->lpi_num = nvecs;
lpic->lpi_free = lpic->lpi_num;
lpic->lpi_col_ids = col_ids;
for (i = 0; i < lpic->lpi_num; i++) {
/*
* Initially all interrupts go to CPU0 but can be moved
* to another CPU by bus_bind_intr() or interrupts shuffling.
*/
lpic->lpi_col_ids[i] = 0;
}
mtx_unlock_spin(&sc->its_dev_lock);
return (0);
}
@ -885,6 +908,7 @@ lpi_free_chunk(struct gic_v3_its_softc *sc, struct lpi_chunk *lpic)
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);
/* First bit of this chunk in a global bitmap */
start = lpic->lpi_base - GIC_FIRST_LPI;
/* and last bit of this chunk... */
@ -892,6 +916,10 @@ lpi_free_chunk(struct gic_v3_its_softc *sc, struct lpi_chunk *lpic)
/* Finally free this chunk */
bit_nclear(bitmap, start, end);
mtx_unlock_spin(&sc->its_dev_lock);
free(lpic->lpi_col_ids, M_GIC_V3_ITS);
lpic->lpi_col_ids = NULL;
}
static void
@ -953,6 +981,32 @@ lpi_xmask_irq(device_t parent, uint32_t irq, boolean_t unmask)
(unmask == TRUE) ? "unmask" : "mask", irq);
}
int
lpi_migrate(device_t parent, uint32_t irq, u_int cpuid)
{
struct gic_v3_its_softc *sc;
struct its_dev *its_dev;
struct its_col *col;
sc = its_sc;
mtx_lock_spin(&sc->its_dev_lock);
its_dev = its_device_find_locked(sc, NULL, irq);
mtx_unlock_spin(&sc->its_dev_lock);
if (its_dev == NULL) {
/* Cannot migrate not configured LPI */
return (ENXIO);
}
/* Find local device's interrupt identifier */
irq = irq - its_dev->lpis.lpi_base;
/* Move interrupt to another collection */
col = sc->its_cols[cpuid];
its_cmd_movi(sc, its_dev, col, irq);
its_dev->lpis.lpi_col_ids[irq] = cpuid;
return (0);
}
void
lpi_unmask_irq(device_t parent, uint32_t irq)
{
@ -1052,6 +1106,20 @@ cmd_fix_endian(struct its_cmd *cmd)
cmd->cmd_dword[i] = htole64(cmd->cmd_dword[i]);
}
static void
its_cmd_movi(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
struct its_col *col, uint32_t id)
{
struct its_cmd_desc desc;
desc.cmd_type = ITS_CMD_MOVI;
desc.cmd_desc_movi.its_dev = its_dev;
desc.cmd_desc_movi.col = col;
desc.cmd_desc_movi.id = id;
its_cmd_send(sc, &desc);
}
static void
its_cmd_mapc(struct gic_v3_its_softc *sc, struct its_col *col, uint8_t valid)
{
@ -1073,9 +1141,15 @@ its_cmd_mapvi(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
uint32_t id, uint32_t pid)
{
struct its_cmd_desc desc;
struct its_col *col;
u_int col_id;
col_id = its_dev->lpis.lpi_col_ids[id];
col = sc->its_cols[col_id];
desc.cmd_type = ITS_CMD_MAPVI;
desc.cmd_desc_mapvi.its_dev = its_dev;
desc.cmd_desc_mapvi.col = col;
desc.cmd_desc_mapvi.id = id;
desc.cmd_desc_mapvi.pid = pid;
@ -1083,14 +1157,23 @@ its_cmd_mapvi(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
}
static void __unused
its_cmd_mapi(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
uint32_t lpinum)
its_cmd_mapi(struct gic_v3_its_softc *sc, struct its_dev *its_dev, uint32_t pid)
{
struct its_cmd_desc desc;
struct its_col *col;
u_int col_id;
uint32_t id;
KASSERT(pid >= its_dev->lpis.lpi_base,
("%s: invalid pid: %d for the ITS device", __func__, pid));
id = pid - its_dev->lpis.lpi_base;
col_id = its_dev->lpis.lpi_col_ids[id];
col = sc->its_cols[col_id];
desc.cmd_type = ITS_CMD_MAPI;
desc.cmd_desc_mapi.its_dev = its_dev;
desc.cmd_desc_mapi.lpinum = lpinum;
desc.cmd_desc_mapi.col = col;
desc.cmd_desc_mapi.pid = pid;
its_cmd_send(sc, &desc);
}
@ -1109,14 +1192,23 @@ its_cmd_mapd(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
}
static void
its_cmd_inv(struct gic_v3_its_softc *sc, struct its_dev *its_dev,
uint32_t lpinum)
its_cmd_inv(struct gic_v3_its_softc *sc, struct its_dev *its_dev, uint32_t pid)
{
struct its_cmd_desc desc;
struct its_col *col;
u_int col_id;
uint32_t id;
KASSERT(pid >= its_dev->lpis.lpi_base,
("%s: invalid pid: %d for the ITS device", __func__, pid));
id = pid - its_dev->lpis.lpi_base;
col_id = its_dev->lpis.lpi_col_ids[id];
col = sc->its_cols[col_id];
desc.cmd_type = ITS_CMD_INV;
desc.cmd_desc_inv.lpinum = lpinum - its_dev->lpis.lpi_base;
desc.cmd_desc_inv.pid = pid - its_dev->lpis.lpi_base;
desc.cmd_desc_inv.its_dev = its_dev;
desc.cmd_desc_inv.col = col;
its_cmd_send(sc, &desc);
}
@ -1216,13 +1308,19 @@ its_cmd_prepare(struct its_cmd *cmd, struct its_cmd_desc *desc)
target = ITS_TARGET_NONE;
switch (cmd_type) {
case ITS_CMD_MOVI: /* Move interrupt ID to another collection */
target = desc->cmd_desc_movi.col->col_target;
cmd_format_command(cmd, ITS_CMD_MOVI);
cmd_format_id(cmd, desc->cmd_desc_movi.id);
cmd_format_col(cmd, desc->cmd_desc_movi.col->col_id);
cmd_format_devid(cmd, desc->cmd_desc_movi.its_dev->devid);
break;
case ITS_CMD_SYNC: /* Wait for previous commands completion */
target = desc->cmd_desc_sync.col->col_target;
cmd_format_command(cmd, ITS_CMD_SYNC);
cmd_format_target(cmd, target);
break;
case ITS_CMD_MAPD: /* Assign ITT to device */
target = desc->cmd_desc_mapd.its_dev->col->col_target;
cmd_format_command(cmd, ITS_CMD_MAPD);
cmd_format_itt(cmd, vtophys(desc->cmd_desc_mapd.its_dev->itt));
/*
@ -1249,25 +1347,25 @@ its_cmd_prepare(struct its_cmd *cmd, struct its_cmd_desc *desc)
cmd_format_target(cmd, target);
break;
case ITS_CMD_MAPVI:
target = desc->cmd_desc_mapvi.its_dev->col->col_target;
target = desc->cmd_desc_mapvi.col->col_target;
cmd_format_command(cmd, ITS_CMD_MAPVI);
cmd_format_devid(cmd, desc->cmd_desc_mapvi.its_dev->devid);
cmd_format_id(cmd, desc->cmd_desc_mapvi.id);
cmd_format_pid(cmd, desc->cmd_desc_mapvi.pid);
cmd_format_col(cmd, desc->cmd_desc_mapvi.its_dev->col->col_id);
cmd_format_col(cmd, desc->cmd_desc_mapvi.col->col_id);
break;
case ITS_CMD_MAPI:
target = desc->cmd_desc_mapi.its_dev->col->col_target;
target = desc->cmd_desc_mapi.col->col_target;
cmd_format_command(cmd, ITS_CMD_MAPI);
cmd_format_devid(cmd, desc->cmd_desc_mapi.its_dev->devid);
cmd_format_id(cmd, desc->cmd_desc_mapi.lpinum);
cmd_format_col(cmd, desc->cmd_desc_mapi.its_dev->col->col_id);
cmd_format_id(cmd, desc->cmd_desc_mapi.pid);
cmd_format_col(cmd, desc->cmd_desc_mapi.col->col_id);
break;
case ITS_CMD_INV:
target = desc->cmd_desc_inv.its_dev->col->col_target;
target = desc->cmd_desc_inv.col->col_target;
cmd_format_command(cmd, ITS_CMD_INV);
cmd_format_devid(cmd, desc->cmd_desc_inv.its_dev->devid);
cmd_format_id(cmd, desc->cmd_desc_inv.lpinum);
cmd_format_id(cmd, desc->cmd_desc_inv.pid);
break;
case ITS_CMD_INVALL:
cmd_format_command(cmd, ITS_CMD_INVALL);
@ -1367,16 +1465,28 @@ its_cmd_send(struct gic_v3_its_softc *sc, struct its_cmd_desc *desc)
return (0);
}
/* Find ITS device descriptor by pci_dev or irq number */
static struct its_dev *
its_device_find_locked(struct gic_v3_its_softc *sc, device_t pci_dev)
its_device_find_locked(struct gic_v3_its_softc *sc, device_t pci_dev,
uint32_t irq)
{
struct its_dev *its_dev;
struct lpi_chunk *lpis;
mtx_assert(&sc->its_dev_lock, MA_OWNED);
KASSERT((pci_dev == NULL || irq == 0),
("%s: Can't search by both pci_dev and irq number", __func__));
/* Find existing device if any */
TAILQ_FOREACH(its_dev, &sc->its_dev_list, entry) {
if (its_dev->pci_dev == pci_dev)
return (its_dev);
if (pci_dev != NULL) {
if (its_dev->pci_dev == pci_dev)
return (its_dev);
} else if (irq != 0) {
lpis = &its_dev->lpis;
if ((irq >= lpis->lpi_base) &&
(irq < (lpis->lpi_base + lpis->lpi_num)))
return (its_dev);
}
}
return (NULL);
@ -1389,13 +1499,12 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
struct its_dev *newdev;
uint64_t typer;
uint32_t devid;
u_int cpuid;
size_t esize;
int err;
mtx_lock_spin(&sc->its_dev_lock);
/* Find existing device if any */
newdev = its_device_find_locked(sc, pci_dev);
newdev = its_device_find_locked(sc, pci_dev, 0);
mtx_unlock_spin(&sc->its_dev_lock);
if (newdev != NULL)
return (newdev);
@ -1410,9 +1519,7 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
newdev->pci_dev = pci_dev;
newdev->devid = devid;
mtx_lock_spin(&sc->its_dev_lock);
err = lpi_alloc_chunk(sc, &newdev->lpis, nvecs);
mtx_unlock_spin(&sc->its_dev_lock);
if (err != 0) {
free(newdev, M_GIC_V3_ITS);
return (NULL);
@ -1429,20 +1536,11 @@ its_device_alloc(struct gic_v3_its_softc *sc, device_t pci_dev,
roundup2(roundup2(nvecs, 2) * esize, 0x100), M_GIC_V3_ITS,
(M_NOWAIT | M_ZERO), 0, ~0UL, 0x100, 0);
if (newdev->itt == 0) {
mtx_lock_spin(&sc->its_dev_lock);
lpi_free_chunk(sc, &newdev->lpis);
mtx_unlock_spin(&sc->its_dev_lock);
free(newdev, M_GIC_V3_ITS);
return (NULL);
}
/*
* Initially all interrupts go to CPU0 but can be moved
* to another CPU by bus_bind_intr() or interrupts shuffling.
*/
cpuid = 0;
newdev->col = sc->its_cols[cpuid];
mtx_lock_spin(&sc->its_dev_lock);
TAILQ_INSERT_TAIL(&sc->its_dev_list, newdev, entry);
mtx_unlock_spin(&sc->its_dev_lock);

View File

@ -96,6 +96,7 @@ 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;
};
/* ITS device */
@ -109,8 +110,6 @@ struct its_dev {
struct lpi_chunk lpis;
/* Virtual address of ITT */
vm_offset_t itt;
/* Interrupt collection */
struct its_col * col;
};
TAILQ_HEAD(its_dev_list, its_dev);
@ -133,6 +132,7 @@ struct its_cmd {
};
/* ITS commands encoding */
#define ITS_CMD_MOVI (0x01)
#define ITS_CMD_SYNC (0x05)
#define ITS_CMD_MAPD (0x08)
#define ITS_CMD_MAPC (0x09)
@ -171,6 +171,12 @@ struct its_cmd_desc {
uint8_t cmd_type;
union {
struct {
struct its_dev *its_dev;
struct its_col *col;
uint32_t id;
} cmd_desc_movi;
struct {
struct its_col *col;
} cmd_desc_sync;
@ -182,13 +188,15 @@ struct its_cmd_desc {
struct {
struct its_dev *its_dev;
struct its_col *col;
uint32_t pid;
uint32_t id;
} cmd_desc_mapvi;
struct {
struct its_dev *its_dev;
uint32_t lpinum;
struct its_col *col;
uint32_t pid;
} cmd_desc_mapi;
struct {
@ -198,7 +206,8 @@ struct its_cmd_desc {
struct {
struct its_dev *its_dev;
uint32_t lpinum;
struct its_col *col;
uint32_t pid;
} cmd_desc_inv;
struct {
@ -257,6 +266,7 @@ int gic_v3_its_map_msi(device_t, device_t, int, uint64_t *, uint32_t *);
int its_init_cpu(struct gic_v3_its_softc *);
int lpi_migrate(device_t, uint32_t, u_int);
void lpi_unmask_irq(device_t, uint32_t);
void lpi_mask_irq(device_t, uint32_t);
/*