summaryrefslogtreecommitdiffstats
path: root/sys/dev/virtio/pci/virtio_pci.c
diff options
context:
space:
mode:
authorgrehan <grehan@FreeBSD.org>2013-07-05 05:47:10 +0000
committergrehan <grehan@FreeBSD.org>2013-07-05 05:47:10 +0000
commit6a7baaf83640e0eaa135d2f7a3c1d4401f1683bf (patch)
tree83ae82cf8f72af4b819f49aa52acdd97f74c6328 /sys/dev/virtio/pci/virtio_pci.c
parent2cb5a953f0a60811764ccaca7c37b51ff007c711 (diff)
parent8e6b84b998690dcfc1a4957dc7a483ac5b600db8 (diff)
downloadFreeBSD-src-6a7baaf83640e0eaa135d2f7a3c1d4401f1683bf.zip
FreeBSD-src-6a7baaf83640e0eaa135d2f7a3c1d4401f1683bf.tar.gz
IFC @ r252763
Diffstat (limited to 'sys/dev/virtio/pci/virtio_pci.c')
-rw-r--r--sys/dev/virtio/pci/virtio_pci.c488
1 files changed, 275 insertions, 213 deletions
diff --git a/sys/dev/virtio/pci/virtio_pci.c b/sys/dev/virtio/pci/virtio_pci.c
index 917ca84..b3df3d9 100644
--- a/sys/dev/virtio/pci/virtio_pci.c
+++ b/sys/dev/virtio/pci/virtio_pci.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -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,36 +127,45 @@ 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_pervq_msix_interrupts(struct vtpci_softc *,
enum intr_type);
-static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
+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_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);
-static int vtpci_legacy_intr(void *);
-static int vtpci_vq_shared_intr(void *);
-static int vtpci_vq_intr(void *);
-static int vtpci_config_intr(void *);
+static void vtpci_legacy_intr(void *);
+static int vtpci_vq_shared_intr_filter(void *);
+static void vtpci_vq_shared_intr(void *);
+static int vtpci_vq_intr_filter(void *);
+static void vtpci_vq_intr(void *);
+static void vtpci_config_intr(void *);
#define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt
@@ -478,18 +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);
- if (flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX)
- sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX;
+ 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);
@@ -506,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);
}
@@ -772,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);
}
@@ -795,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);
@@ -815,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++;
}
@@ -869,54 +873,62 @@ 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_intr_resources(struct vtpci_softc *sc)
+vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags,
+ struct vtpci_interrupt *intr)
{
- device_t dev;
struct resource *irq;
- struct vtpci_virtqueue *vqx;
- int i, rid, flags, res_idx;
- dev = sc->vtpci_dev;
+ irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags);
+ if (irq == NULL)
+ return (ENXIO);
- if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) {
- rid = 0;
- flags = RF_ACTIVE | RF_SHAREABLE;
- } else {
- rid = 1;
- flags = RF_ACTIVE;
- }
+ intr->vti_irq = irq;
+ intr->vti_rid = rid;
- 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);
+ return (0);
+}
- sc->vtpci_intr_res[i].irq = irq;
- sc->vtpci_intr_res[i].rid = rid++;
- }
+static int
+vtpci_alloc_intr_resources(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, rid, flags, nvq_intrs, error;
+
+ rid = 0;
+ flags = RF_ACTIVE;
+
+ if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
+ flags |= RF_SHAREABLE;
+ else
+ rid = 1;
/*
- * 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];
-
- 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;
+ 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);
+
+ /* 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);
@@ -925,67 +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, vtpci_legacy_intr, NULL,
- 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_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
+vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
{
- device_t dev;
- struct vtpci_intr_resource *ires;
struct vtpci_virtqueue *vqx;
+ struct vtpci_interrupt *intr;
int i, error;
- dev = sc->vtpci_dev;
+ intr = sc->vtpci_msix_vq_interrupts;
- /*
- * The first resource is used for configuration changed interrupts.
- */
- ires = &sc->vtpci_intr_res[0];
- error = bus_setup_intr(dev, ires->irq, type, vtpci_config_intr,
- NULL, sc, &ires->intrhand);
- if (error)
- return (error);
+ for (i = 0; i < sc->vtpci_nvqs; i++) {
+ vqx = &sc->vtpci_vqs[i];
- if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
- ires = &sc->vtpci_intr_res[1];
+ if (vqx->vtv_no_intr)
+ continue;
- error = bus_setup_intr(dev, ires->irq, type,
- vtpci_vq_shared_intr, NULL, sc, &ires->intrhand);
+ 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);
- } else {
- /*
- * Each remaining resource is assigned to a specific virtqueue.
- */
- for (i = 0; i < sc->vtpci_nvqs; i++) {
- vqx = &sc->vtpci_vqx[i];
- if (vqx->ires_idx < 1)
- continue;
-
- ires = &sc->vtpci_intr_res[vqx->ires_idx];
- error = bus_setup_intr(dev, ires->irq, type,
- vtpci_vq_intr, NULL, vqx->vq, &ires->intrhand);
- if (error)
- return (error);
- }
+
+ intr++;
}
- error = vtpci_set_host_msix_vectors(sc);
+ return (0);
+}
+
+static int
+vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
+{
+ device_t dev;
+ struct vtpci_interrupt *intr;
+ int error;
+
+ dev = sc->vtpci_dev;
+ intr = &sc->vtpci_device_interrupt;
+
+ error = bus_setup_intr(dev, intr->vti_irq, type, NULL,
+ vtpci_config_intr, sc, &intr->vti_handler);
if (error)
return (error);
- return (0);
+ if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
+ 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,
+ &intr->vti_handler);
+ } else
+ error = vtpci_setup_pervq_msix_interrupts(sc, type);
+
+ return (error ? error : vtpci_set_host_msix_vectors(sc));
}
static int
@@ -995,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)
@@ -1012,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);
@@ -1051,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;
+
+ intr = &sc->vtpci_device_interrupt;
+ offset = VIRTIO_MSI_CONFIG_VECTOR;
- error = vtpci_register_msix_vector(sc, VIRTIO_MSI_CONFIG_VECTOR, 0);
+ 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
@@ -1079,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);
@@ -1098,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;
+ }
+}
- if (ires->irq != NULL) {
- bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
- ires->irq);
- ires->irq = NULL;
- }
+static void
+vtpci_free_interrupts(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, nvq_intrs;
- ires->rid = -1;
+ 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 (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;
}
@@ -1134,19 +1167,32 @@ 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];
+
+ vtpci_select_virtqueue(sc, idx);
+ vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0);
- virtqueue_free(vqx->vq);
- vqx->vq = NULL;
+ 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)
{
int idx;
@@ -1166,14 +1212,6 @@ vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *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)
{
@@ -1191,7 +1229,7 @@ vtpci_select_virtqueue(struct vtpci_softc *sc, int idx)
vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx);
}
-static int
+static void
vtpci_legacy_intr(void *xsc)
{
struct vtpci_softc *sc;
@@ -1200,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);
@@ -1208,15 +1246,16 @@ vtpci_legacy_intr(void *xsc)
if (isr & VIRTIO_PCI_ISR_CONFIG)
vtpci_config_intr(sc);
- if (isr & VIRTIO_PCI_ISR_INTR)
- for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
- virtqueue_intr(vqx->vq);
-
- return (isr ? FILTER_HANDLED : FILTER_STRAY);
+ if (isr & VIRTIO_PCI_ISR_INTR) {
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
+ }
}
static int
-vtpci_vq_shared_intr(void *xsc)
+vtpci_vq_shared_intr_filter(void *xsc)
{
struct vtpci_softc *sc;
struct vtpci_virtqueue *vqx;
@@ -1224,39 +1263,62 @@ vtpci_vq_shared_intr(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(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_HANDLED : FILTER_STRAY);
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
+}
+
+static void
+vtpci_vq_shared_intr(void *xsc)
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ int i;
+
+ sc = xsc;
+ vqx = &sc->vtpci_vqs[0];
+
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
}
static int
-vtpci_vq_intr(void *xvq)
+vtpci_vq_intr_filter(void *xvq)
{
struct virtqueue *vq;
int rc;
vq = xvq;
- rc = virtqueue_intr(vq);
+ rc = virtqueue_intr_filter(vq);
- return (rc ? FILTER_HANDLED : FILTER_STRAY);
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
}
-static int
+static void
+vtpci_vq_intr(void *xvq)
+{
+ struct virtqueue *vq;
+
+ vq = xvq;
+ virtqueue_intr(vq);
+}
+
+static void
vtpci_config_intr(void *xsc)
{
struct vtpci_softc *sc;
device_t child;
- int rc;
- rc = 0;
sc = xsc;
child = sc->vtpci_child_dev;
if (child != NULL)
- rc = VIRTIO_CONFIG_CHANGE(child);
-
- return (rc ? FILTER_HANDLED : FILTER_STRAY);
+ VIRTIO_CONFIG_CHANGE(child);
}
OpenPOWER on IntegriCloud