Merge virtio_pci changes from projects/virtio
This commit is primarily a significant cleanup to the interrupt allocation code that had gotten a bit jumbled from having to support per-vq MSIX, shared MSIX, MSI, and legacy style interrupts. Contains projects/virtio commits: r246064: virtio_pci: Rewrite allocation of interrupts r246065: virtio_pci: Remove spaces before a tab r246066: virtio_pci: Dynamically allocate the virtqueue array r246304: virtio_pci: Clean up after failed virtqueue alloc attempt r246305: virtio_pci: Move no interrupt check into the PCI interrupt handlers r246308: virtio_pci: Remove unused variable MFC after: 1 month
This commit is contained in:
parent
abd6790ce8
commit
62a69c4153
@ -51,6 +51,17 @@ __FBSDID("$FreeBSD$");
|
||||
#include "virtio_bus_if.h"
|
||||
#include "virtio_if.h"
|
||||
|
||||
struct vtpci_interrupt {
|
||||
struct resource *vti_irq;
|
||||
int vti_rid;
|
||||
void *vti_handler;
|
||||
};
|
||||
|
||||
struct vtpci_virtqueue {
|
||||
struct virtqueue *vtv_vq;
|
||||
int vtv_no_intr;
|
||||
};
|
||||
|
||||
struct vtpci_softc {
|
||||
device_t vtpci_dev;
|
||||
struct resource *vtpci_res;
|
||||
@ -69,40 +80,22 @@ struct vtpci_softc {
|
||||
device_t vtpci_child_dev;
|
||||
struct virtio_feature_desc *vtpci_child_feat_desc;
|
||||
|
||||
/*
|
||||
* Ideally, each virtqueue that the driver provides a callback for
|
||||
* will receive its own MSIX vector. If there are not sufficient
|
||||
* vectors available, we will then attempt to have all the VQs
|
||||
* share one vector. Note that when using MSIX, the configuration
|
||||
* changed notifications must be on their own vector.
|
||||
*
|
||||
* If MSIX is not available, we will attempt to have the whole
|
||||
* device share one MSI vector, and then, finally, one legacy
|
||||
* interrupt.
|
||||
*/
|
||||
int vtpci_nvqs;
|
||||
struct vtpci_virtqueue {
|
||||
struct virtqueue *vq;
|
||||
/* Device did not provide a callback for this virtqueue. */
|
||||
int no_intr;
|
||||
/* Index into vtpci_intr_res[] below. Unused, then -1. */
|
||||
int ires_idx;
|
||||
} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
|
||||
struct vtpci_virtqueue *vtpci_vqs;
|
||||
|
||||
/*
|
||||
* When using MSIX interrupts, the first element of vtpci_intr_res[]
|
||||
* is always the configuration changed notifications. The remaining
|
||||
* element(s) are used for the virtqueues.
|
||||
* Ideally, each virtqueue that the driver provides a callback for will
|
||||
* receive its own MSIX vector. If there are not sufficient vectors
|
||||
* available, then attempt to have all the VQs share one vector. For
|
||||
* MSIX, the configuration changed notifications must be on their own
|
||||
* vector.
|
||||
*
|
||||
* With MSI and legacy interrupts, only the first element of
|
||||
* vtpci_intr_res[] is used.
|
||||
* If MSIX is not available, we will attempt to have the whole device
|
||||
* share one MSI vector, and then, finally, one legacy interrupt.
|
||||
*/
|
||||
int vtpci_nintr_res;
|
||||
struct vtpci_intr_resource {
|
||||
struct resource *irq;
|
||||
int rid;
|
||||
void *intrhand;
|
||||
} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
|
||||
struct vtpci_interrupt vtpci_device_interrupt;
|
||||
struct vtpci_interrupt *vtpci_msix_vq_interrupts;
|
||||
int vtpci_nmsix_resources;
|
||||
};
|
||||
|
||||
static int vtpci_probe(device_t);
|
||||
@ -134,28 +127,35 @@ static void vtpci_describe_features(struct vtpci_softc *, const char *,
|
||||
uint64_t);
|
||||
static void vtpci_probe_and_attach_child(struct vtpci_softc *);
|
||||
|
||||
static int vtpci_alloc_msix(struct vtpci_softc *, int);
|
||||
static int vtpci_alloc_msi(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msi(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_legacy(struct vtpci_softc *);
|
||||
static int vtpci_alloc_msix(struct vtpci_softc *, int);
|
||||
static int vtpci_alloc_msi(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_msi(struct vtpci_softc *);
|
||||
static int vtpci_alloc_intr_legacy(struct vtpci_softc *);
|
||||
static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int,
|
||||
struct vtpci_interrupt *);
|
||||
static int vtpci_alloc_intr_resources(struct vtpci_softc *);
|
||||
|
||||
static int vtpci_setup_legacy_interrupt(struct vtpci_softc *,
|
||||
static int vtpci_setup_legacy_interrupt(struct vtpci_softc *,
|
||||
enum intr_type);
|
||||
static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
|
||||
static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *,
|
||||
enum intr_type);
|
||||
static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type);
|
||||
static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
|
||||
enum intr_type);
|
||||
static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type);
|
||||
|
||||
static int vtpci_register_msix_vector(struct vtpci_softc *, int, int);
|
||||
static int vtpci_set_host_msix_vectors(struct vtpci_softc *);
|
||||
static int vtpci_reinit_virtqueue(struct vtpci_softc *, int);
|
||||
static int vtpci_register_msix_vector(struct vtpci_softc *, int,
|
||||
struct vtpci_interrupt *);
|
||||
static int vtpci_set_host_msix_vectors(struct vtpci_softc *);
|
||||
static int vtpci_reinit_virtqueue(struct vtpci_softc *, int);
|
||||
|
||||
static void vtpci_free_interrupt(struct vtpci_softc *,
|
||||
struct vtpci_interrupt *);
|
||||
static void vtpci_free_interrupts(struct vtpci_softc *);
|
||||
static void vtpci_free_virtqueues(struct vtpci_softc *);
|
||||
static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *);
|
||||
static void vtpci_release_child_resources(struct vtpci_softc *);
|
||||
static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *);
|
||||
static void vtpci_reset(struct vtpci_softc *);
|
||||
|
||||
static void vtpci_select_virtqueue(struct vtpci_softc *, int);
|
||||
@ -480,15 +480,19 @@ vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
|
||||
uint16_t size;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
error = 0;
|
||||
|
||||
if (sc->vtpci_nvqs != 0)
|
||||
return (EALREADY);
|
||||
if (nvqs <= 0 || nvqs > VIRTIO_MAX_VIRTQUEUES)
|
||||
if (nvqs <= 0)
|
||||
return (EINVAL);
|
||||
|
||||
sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue),
|
||||
M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (sc->vtpci_vqs == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
for (idx = 0; idx < nvqs; idx++) {
|
||||
vqx = &sc->vtpci_vqx[idx];
|
||||
vqx = &sc->vtpci_vqs[idx];
|
||||
info = &vq_info[idx];
|
||||
|
||||
vtpci_select_virtqueue(sc, idx);
|
||||
@ -505,12 +509,15 @@ vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
|
||||
vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
|
||||
virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
|
||||
|
||||
vqx->vq = *info->vqai_vq = vq;
|
||||
vqx->no_intr = info->vqai_intr == NULL;
|
||||
vqx->vtv_vq = *info->vqai_vq = vq;
|
||||
vqx->vtv_no_intr = info->vqai_intr == NULL;
|
||||
|
||||
sc->vtpci_nvqs++;
|
||||
}
|
||||
|
||||
if (error)
|
||||
vtpci_free_virtqueues(sc);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -771,7 +778,7 @@ vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
|
||||
|
||||
cnt = required;
|
||||
if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
|
||||
sc->vtpci_nintr_res = required;
|
||||
sc->vtpci_nmsix_resources = required;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -794,10 +801,8 @@ vtpci_alloc_msi(struct vtpci_softc *sc)
|
||||
return (1);
|
||||
|
||||
cnt = required;
|
||||
if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) {
|
||||
sc->vtpci_nintr_res = required;
|
||||
if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required)
|
||||
return (0);
|
||||
}
|
||||
|
||||
pci_release_msi(dev);
|
||||
|
||||
@ -814,7 +819,7 @@ vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc)
|
||||
return (ENOTSUP);
|
||||
|
||||
for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) {
|
||||
if (sc->vtpci_vqx[i].no_intr == 0)
|
||||
if (sc->vtpci_vqs[i].vtv_no_intr == 0)
|
||||
nvectors++;
|
||||
}
|
||||
|
||||
@ -868,7 +873,22 @@ vtpci_alloc_intr_legacy(struct vtpci_softc *sc)
|
||||
{
|
||||
|
||||
sc->vtpci_flags |= VTPCI_FLAG_LEGACY;
|
||||
sc->vtpci_nintr_res = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags,
|
||||
struct vtpci_interrupt *intr)
|
||||
{
|
||||
struct resource *irq;
|
||||
|
||||
irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags);
|
||||
if (irq == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
intr->vti_irq = irq;
|
||||
intr->vti_rid = rid;
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -876,46 +896,39 @@ vtpci_alloc_intr_legacy(struct vtpci_softc *sc)
|
||||
static int
|
||||
vtpci_alloc_intr_resources(struct vtpci_softc *sc)
|
||||
{
|
||||
device_t dev;
|
||||
struct resource *irq;
|
||||
struct vtpci_virtqueue *vqx;
|
||||
int i, rid, flags, res_idx;
|
||||
struct vtpci_interrupt *intr;
|
||||
int i, rid, flags, nvq_intrs, error;
|
||||
|
||||
dev = sc->vtpci_dev;
|
||||
rid = 0;
|
||||
flags = RF_ACTIVE;
|
||||
|
||||
if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) {
|
||||
rid = 0;
|
||||
flags = RF_ACTIVE | RF_SHAREABLE;
|
||||
} else {
|
||||
if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
|
||||
flags |= RF_SHAREABLE;
|
||||
else
|
||||
rid = 1;
|
||||
flags = RF_ACTIVE;
|
||||
}
|
||||
|
||||
for (i = 0; i < sc->vtpci_nintr_res; i++) {
|
||||
irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
|
||||
if (irq == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
sc->vtpci_intr_res[i].irq = irq;
|
||||
sc->vtpci_intr_res[i].rid = rid++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the virtqueue into the correct index in vq_intr_res[]. The
|
||||
* first index is reserved for configuration changed notifications.
|
||||
* For legacy and MSI interrupts, this single resource handles all
|
||||
* interrupts. For MSIX, this resource is used for the configuration
|
||||
* changed interrupt.
|
||||
*/
|
||||
for (i = 0, res_idx = 1; i < sc->vtpci_nvqs; i++) {
|
||||
vqx = &sc->vtpci_vqx[i];
|
||||
intr = &sc->vtpci_device_interrupt;
|
||||
error = vtpci_alloc_interrupt(sc, rid, flags, intr);
|
||||
if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI))
|
||||
return (error);
|
||||
|
||||
if (sc->vtpci_flags & VTPCI_FLAG_MSIX) {
|
||||
if (vqx->no_intr != 0)
|
||||
vqx->ires_idx = -1;
|
||||
else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX)
|
||||
vqx->ires_idx = res_idx;
|
||||
else
|
||||
vqx->ires_idx = res_idx++;
|
||||
} else
|
||||
vqx->ires_idx = -1;
|
||||
/* Subtract one for the configuration changed interrupt. */
|
||||
nvq_intrs = sc->vtpci_nmsix_resources - 1;
|
||||
|
||||
intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs *
|
||||
sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (sc->vtpci_msix_vq_interrupts == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) {
|
||||
error = vtpci_alloc_interrupt(sc, rid, flags, intr);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
@ -924,63 +937,67 @@ vtpci_alloc_intr_resources(struct vtpci_softc *sc)
|
||||
static int
|
||||
vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type)
|
||||
{
|
||||
device_t dev;
|
||||
struct vtpci_intr_resource *ires;
|
||||
struct vtpci_interrupt *intr;
|
||||
int error;
|
||||
|
||||
dev = sc->vtpci_dev;
|
||||
|
||||
ires = &sc->vtpci_intr_res[0];
|
||||
error = bus_setup_intr(dev, ires->irq, type, NULL, vtpci_legacy_intr,
|
||||
sc, &ires->intrhand);
|
||||
intr = &sc->vtpci_device_interrupt;
|
||||
error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL,
|
||||
vtpci_legacy_intr, sc, &intr->vti_handler);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
|
||||
{
|
||||
struct vtpci_virtqueue *vqx;
|
||||
struct vtpci_interrupt *intr;
|
||||
int i, error;
|
||||
|
||||
intr = sc->vtpci_msix_vq_interrupts;
|
||||
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++) {
|
||||
vqx = &sc->vtpci_vqs[i];
|
||||
|
||||
if (vqx->vtv_no_intr)
|
||||
continue;
|
||||
|
||||
error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type,
|
||||
vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq,
|
||||
&intr->vti_handler);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
intr++;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
|
||||
{
|
||||
device_t dev;
|
||||
struct vtpci_intr_resource *ires;
|
||||
struct vtpci_virtqueue *vqx;
|
||||
int i, error;
|
||||
struct vtpci_interrupt *intr;
|
||||
int error;
|
||||
|
||||
dev = sc->vtpci_dev;
|
||||
intr = &sc->vtpci_device_interrupt;
|
||||
|
||||
/*
|
||||
* The first MSIX vector is used for configuration changed interrupts.
|
||||
*/
|
||||
ires = &sc->vtpci_intr_res[0];
|
||||
error = bus_setup_intr(dev, ires->irq, type, NULL, vtpci_config_intr,
|
||||
sc, &ires->intrhand);
|
||||
error = bus_setup_intr(dev, intr->vti_irq, type, NULL,
|
||||
vtpci_config_intr, sc, &intr->vti_handler);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
|
||||
ires = &sc->vtpci_intr_res[1];
|
||||
|
||||
error = bus_setup_intr(dev, ires->irq, type,
|
||||
intr = sc->vtpci_msix_vq_interrupts;
|
||||
error = bus_setup_intr(dev, intr->vti_irq, type,
|
||||
vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc,
|
||||
&ires->intrhand);
|
||||
} else {
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++) {
|
||||
vqx = &sc->vtpci_vqx[i];
|
||||
if (vqx->ires_idx < 1)
|
||||
continue;
|
||||
&intr->vti_handler);
|
||||
} else
|
||||
error = vtpci_setup_pervq_msix_interrupts(sc, type);
|
||||
|
||||
ires = &sc->vtpci_intr_res[vqx->ires_idx];
|
||||
error = bus_setup_intr(dev, ires->irq, type,
|
||||
vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vq,
|
||||
&ires->intrhand);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error == 0)
|
||||
error = vtpci_set_host_msix_vectors(sc);
|
||||
|
||||
return (error);
|
||||
return (error ? error : vtpci_set_host_msix_vectors(sc));
|
||||
}
|
||||
|
||||
static int
|
||||
@ -990,7 +1007,7 @@ vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type)
|
||||
|
||||
type |= INTR_MPSAFE;
|
||||
KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK,
|
||||
("no interrupt type selected: %#x", sc->vtpci_flags));
|
||||
("%s: no interrupt type selected %#x", __func__, sc->vtpci_flags));
|
||||
|
||||
error = vtpci_alloc_intr_resources(sc);
|
||||
if (error)
|
||||
@ -1007,34 +1024,24 @@ vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type)
|
||||
}
|
||||
|
||||
static int
|
||||
vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
|
||||
vtpci_register_msix_vector(struct vtpci_softc *sc, int offset,
|
||||
struct vtpci_interrupt *intr)
|
||||
{
|
||||
device_t dev;
|
||||
uint16_t vector, rdvector;
|
||||
uint16_t vector;
|
||||
|
||||
dev = sc->vtpci_dev;
|
||||
|
||||
if (res_idx != -1) {
|
||||
if (intr != NULL) {
|
||||
/* Map from guest rid to host vector. */
|
||||
vector = sc->vtpci_intr_res[res_idx].rid - 1;
|
||||
vector = intr->vti_rid - 1;
|
||||
} else
|
||||
vector = VIRTIO_MSI_NO_VECTOR;
|
||||
|
||||
/*
|
||||
* Assert the first resource is always used for the configuration
|
||||
* changed interrupts.
|
||||
*/
|
||||
if (res_idx == 0) {
|
||||
KASSERT(vector == 0 && offset == VIRTIO_MSI_CONFIG_VECTOR,
|
||||
("bad first res use vector:%d offset:%d", vector, offset));
|
||||
} else
|
||||
KASSERT(offset == VIRTIO_MSI_QUEUE_VECTOR, ("bad offset"));
|
||||
|
||||
vtpci_write_config_2(sc, offset, vector);
|
||||
|
||||
/* Read vector to determine if the host had sufficient resources. */
|
||||
rdvector = vtpci_read_config_2(sc, offset);
|
||||
if (rdvector != vector) {
|
||||
if (vtpci_read_config_2(sc, offset) != vector) {
|
||||
device_printf(dev,
|
||||
"insufficient host resources for MSIX interrupts\n");
|
||||
return (ENODEV);
|
||||
@ -1046,24 +1053,40 @@ vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
|
||||
static int
|
||||
vtpci_set_host_msix_vectors(struct vtpci_softc *sc)
|
||||
{
|
||||
struct vtpci_virtqueue *vqx;
|
||||
int idx, error;
|
||||
struct vtpci_interrupt *intr, *tintr;
|
||||
int idx, offset, error;
|
||||
|
||||
error = vtpci_register_msix_vector(sc, VIRTIO_MSI_CONFIG_VECTOR, 0);
|
||||
intr = &sc->vtpci_device_interrupt;
|
||||
offset = VIRTIO_MSI_CONFIG_VECTOR;
|
||||
|
||||
error = vtpci_register_msix_vector(sc, offset, intr);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
|
||||
vqx = &sc->vtpci_vqx[idx];
|
||||
intr = sc->vtpci_msix_vq_interrupts;
|
||||
offset = VIRTIO_MSI_QUEUE_VECTOR;
|
||||
|
||||
for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
|
||||
vtpci_select_virtqueue(sc, idx);
|
||||
error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
|
||||
vqx->ires_idx);
|
||||
|
||||
if (sc->vtpci_vqs[idx].vtv_no_intr)
|
||||
tintr = NULL;
|
||||
else
|
||||
tintr = intr;
|
||||
|
||||
error = vtpci_register_msix_vector(sc, offset, tintr);
|
||||
if (error)
|
||||
return (error);
|
||||
break;
|
||||
|
||||
/*
|
||||
* For shared MSIX, all the virtqueues share the first
|
||||
* interrupt.
|
||||
*/
|
||||
if ((sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0)
|
||||
intr++;
|
||||
}
|
||||
|
||||
return (0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1074,10 +1097,10 @@ vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx)
|
||||
int error;
|
||||
uint16_t size;
|
||||
|
||||
vqx = &sc->vtpci_vqx[idx];
|
||||
vq = vqx->vq;
|
||||
vqx = &sc->vtpci_vqs[idx];
|
||||
vq = vqx->vtv_vq;
|
||||
|
||||
KASSERT(vq != NULL, ("vq %d not allocated", idx));
|
||||
KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx));
|
||||
|
||||
vtpci_select_virtqueue(sc, idx);
|
||||
size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
|
||||
@ -1093,35 +1116,50 @@ vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx)
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_free_interrupts(struct vtpci_softc *sc)
|
||||
vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr)
|
||||
{
|
||||
device_t dev;
|
||||
struct vtpci_intr_resource *ires;
|
||||
int i;
|
||||
|
||||
dev = sc->vtpci_dev;
|
||||
|
||||
for (i = 0; i < sc->vtpci_nintr_res; i++) {
|
||||
ires = &sc->vtpci_intr_res[i];
|
||||
if (intr->vti_handler != NULL) {
|
||||
bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler);
|
||||
intr->vti_handler = NULL;
|
||||
}
|
||||
|
||||
if (ires->intrhand != NULL) {
|
||||
bus_teardown_intr(dev, ires->irq, ires->intrhand);
|
||||
ires->intrhand = NULL;
|
||||
if (intr->vti_irq != NULL) {
|
||||
bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid,
|
||||
intr->vti_irq);
|
||||
intr->vti_irq = NULL;
|
||||
intr->vti_rid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_free_interrupts(struct vtpci_softc *sc)
|
||||
{
|
||||
struct vtpci_interrupt *intr;
|
||||
int i, nvq_intrs;
|
||||
|
||||
vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt);
|
||||
|
||||
if (sc->vtpci_nmsix_resources != 0) {
|
||||
nvq_intrs = sc->vtpci_nmsix_resources - 1;
|
||||
sc->vtpci_nmsix_resources = 0;
|
||||
|
||||
intr = sc->vtpci_msix_vq_interrupts;
|
||||
if (intr != NULL) {
|
||||
for (i = 0; i < nvq_intrs; i++, intr++)
|
||||
vtpci_free_interrupt(sc, intr);
|
||||
|
||||
free(sc->vtpci_msix_vq_interrupts, M_DEVBUF);
|
||||
sc->vtpci_msix_vq_interrupts = NULL;
|
||||
}
|
||||
|
||||
if (ires->irq != NULL) {
|
||||
bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
|
||||
ires->irq);
|
||||
ires->irq = NULL;
|
||||
}
|
||||
|
||||
ires->rid = -1;
|
||||
}
|
||||
|
||||
if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX))
|
||||
pci_release_msi(dev);
|
||||
pci_release_msi(sc->vtpci_dev);
|
||||
|
||||
sc->vtpci_nintr_res = 0;
|
||||
sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK;
|
||||
}
|
||||
|
||||
@ -1129,18 +1167,31 @@ static void
|
||||
vtpci_free_virtqueues(struct vtpci_softc *sc)
|
||||
{
|
||||
struct vtpci_virtqueue *vqx;
|
||||
int i;
|
||||
int idx;
|
||||
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++) {
|
||||
vqx = &sc->vtpci_vqx[i];
|
||||
for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
|
||||
vqx = &sc->vtpci_vqs[idx];
|
||||
|
||||
virtqueue_free(vqx->vq);
|
||||
vqx->vq = NULL;
|
||||
vtpci_select_virtqueue(sc, idx);
|
||||
vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0);
|
||||
|
||||
virtqueue_free(vqx->vtv_vq);
|
||||
vqx->vtv_vq = NULL;
|
||||
}
|
||||
|
||||
free(sc->vtpci_vqs, M_DEVBUF);
|
||||
sc->vtpci_vqs = NULL;
|
||||
sc->vtpci_nvqs = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_release_child_resources(struct vtpci_softc *sc)
|
||||
{
|
||||
|
||||
vtpci_free_interrupts(sc);
|
||||
vtpci_free_virtqueues(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc)
|
||||
{
|
||||
@ -1160,14 +1211,6 @@ vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc)
|
||||
vtpci_free_interrupts(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_release_child_resources(struct vtpci_softc *sc)
|
||||
{
|
||||
|
||||
vtpci_free_interrupts(sc);
|
||||
vtpci_free_virtqueues(sc);
|
||||
}
|
||||
|
||||
static void
|
||||
vtpci_reset(struct vtpci_softc *sc)
|
||||
{
|
||||
@ -1195,7 +1238,7 @@ vtpci_legacy_intr(void *xsc)
|
||||
uint8_t isr;
|
||||
|
||||
sc = xsc;
|
||||
vqx = &sc->vtpci_vqx[0];
|
||||
vqx = &sc->vtpci_vqs[0];
|
||||
|
||||
/* Reading the ISR also clears it. */
|
||||
isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
|
||||
@ -1204,8 +1247,10 @@ vtpci_legacy_intr(void *xsc)
|
||||
vtpci_config_intr(sc);
|
||||
|
||||
if (isr & VIRTIO_PCI_ISR_INTR) {
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
|
||||
virtqueue_intr(vqx->vq);
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
|
||||
if (vqx->vtv_no_intr == 0)
|
||||
virtqueue_intr(vqx->vtv_vq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1218,10 +1263,12 @@ vtpci_vq_shared_intr_filter(void *xsc)
|
||||
|
||||
rc = 0;
|
||||
sc = xsc;
|
||||
vqx = &sc->vtpci_vqx[0];
|
||||
vqx = &sc->vtpci_vqs[0];
|
||||
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
|
||||
rc |= virtqueue_intr_filter(vqx->vq);
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
|
||||
if (vqx->vtv_no_intr == 0)
|
||||
rc |= virtqueue_intr_filter(vqx->vtv_vq);
|
||||
}
|
||||
|
||||
return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
|
||||
}
|
||||
@ -1234,10 +1281,12 @@ vtpci_vq_shared_intr(void *xsc)
|
||||
int i;
|
||||
|
||||
sc = xsc;
|
||||
vqx = &sc->vtpci_vqx[0];
|
||||
vqx = &sc->vtpci_vqs[0];
|
||||
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
|
||||
virtqueue_intr(vqx->vq);
|
||||
for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
|
||||
if (vqx->vtv_no_intr == 0)
|
||||
virtqueue_intr(vqx->vtv_vq);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -70,11 +70,6 @@ struct vq_alloc_info;
|
||||
#define VIRTIO_TRANSPORT_F_START 28
|
||||
#define VIRTIO_TRANSPORT_F_END 32
|
||||
|
||||
/*
|
||||
* Maximum number of virtqueues per device.
|
||||
*/
|
||||
#define VIRTIO_MAX_VIRTQUEUES 8
|
||||
|
||||
/*
|
||||
* Each virtqueue indirect descriptor list must be physically contiguous.
|
||||
* To allow us to malloc(9) each list individually, limit the number
|
||||
|
@ -417,8 +417,6 @@ int
|
||||
virtqueue_intr_filter(struct virtqueue *vq)
|
||||
{
|
||||
|
||||
if (__predict_false(vq->vq_intrhand == NULL))
|
||||
return (0);
|
||||
if (vq->vq_used_cons_idx == vq->vq_ring.used->idx)
|
||||
return (0);
|
||||
|
||||
@ -431,8 +429,7 @@ void
|
||||
virtqueue_intr(struct virtqueue *vq)
|
||||
{
|
||||
|
||||
if (__predict_true(vq->vq_intrhand != NULL))
|
||||
vq->vq_intrhand(vq->vq_intrhand_arg);
|
||||
vq->vq_intrhand(vq->vq_intrhand_arg);
|
||||
}
|
||||
|
||||
int
|
||||
|
Loading…
x
Reference in New Issue
Block a user