Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Support MSI-X in virtio



details:   https://anonhg.NetBSD.org/src/rev/6e3d7a301c41
branches:  trunk
changeset: 341204:6e3d7a301c41
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Mon Oct 26 01:44:48 2015 +0000

description:
Support MSI-X in virtio

Currently only vioif(4) uses the feature.

knakahara@ helped to migrate to pci_intr_alloc(9). Thanks!

diffstat:

 sys/dev/pci/if_vioif.c  |   71 +++++++----
 sys/dev/pci/virtio.c    |  282 +++++++++++++++++++++++++++++++++++++++++++----
 sys/dev/pci/virtioreg.h |    5 +-
 sys/dev/pci/virtiovar.h |    7 +-
 4 files changed, 306 insertions(+), 59 deletions(-)

diffs (truncated from 523 to 300 lines):

diff -r f7cc68cd173d -r 6e3d7a301c41 sys/dev/pci/if_vioif.c
--- a/sys/dev/pci/if_vioif.c    Sun Oct 25 23:00:00 2015 +0000
+++ b/sys/dev/pci/if_vioif.c    Mon Oct 26 01:44:48 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_vioif.c,v 1.16 2015/05/05 10:56:13 ozaki-r Exp $    */
+/*     $NetBSD: if_vioif.c,v 1.17 2015/10/26 01:44:48 ozaki-r Exp $    */
 
 /*
  * Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.16 2015/05/05 10:56:13 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.17 2015/10/26 01:44:48 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -487,6 +487,7 @@
        uint32_t features;
        struct ifnet *ifp = &sc->sc_ethercom.ec_if;
        u_int flags;
+       int r;
 
        if (vsc->sc_child != NULL) {
                aprint_normal(": child already attached for %s; "
@@ -511,6 +512,7 @@
 #ifdef VIOIF_SOFTINT_INTR
        vsc->sc_flags |= VIRTIO_F_PCI_INTR_SOFTINT;
 #endif
+       vsc->sc_flags |= VIRTIO_F_PCI_INTR_MSIX;
 
        features = virtio_negotiate_features(vsc,
                                             (VIRTIO_NET_F_MAC |
@@ -560,21 +562,6 @@
        aprint_normal(": Ethernet address %s\n", ether_sprintf(sc->sc_mac));
        aprint_naive("\n");
 
-       if (virtio_alloc_vq(vsc, &sc->sc_vq[0], 0,
-                           MCLBYTES+sizeof(struct virtio_net_hdr), 2,
-                           "rx") != 0) {
-               goto err;
-       }
-       vsc->sc_nvqs = 1;
-       sc->sc_vq[0].vq_done = vioif_rx_vq_done;
-       if (virtio_alloc_vq(vsc, &sc->sc_vq[1], 1,
-                           (sizeof(struct virtio_net_hdr)
-                            + (ETHER_MAX_LEN - ETHER_HDR_LEN)),
-                           VIRTIO_NET_TX_MAXNSEGS + 1,
-                           "tx") != 0) {
-               goto err;
-       }
-
 #ifdef VIOIF_MPSAFE
        sc->sc_tx_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
        sc->sc_rx_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
@@ -584,23 +571,51 @@
 #endif
        sc->sc_stopping = false;
 
+       /*
+        * Allocating a virtqueue for Rx
+        */
+       r = virtio_alloc_vq(vsc, &sc->sc_vq[0], 0,
+           MCLBYTES+sizeof(struct virtio_net_hdr), 2, "rx");
+       if (r != 0)
+               goto err;
+       vsc->sc_nvqs = 1;
+       sc->sc_vq[0].vq_done = vioif_rx_vq_done;
+
+       /*
+        * Allocating a virtqueue for Tx
+        */
+       r = virtio_alloc_vq(vsc, &sc->sc_vq[1], 1,
+           (sizeof(struct virtio_net_hdr) + (ETHER_MAX_LEN - ETHER_HDR_LEN)),
+           VIRTIO_NET_TX_MAXNSEGS + 1, "tx");
+       if (r != 0)
+               goto err;
        vsc->sc_nvqs = 2;
        sc->sc_vq[1].vq_done = vioif_tx_vq_done;
+
        virtio_start_vq_intr(vsc, &sc->sc_vq[0]);
        virtio_stop_vq_intr(vsc, &sc->sc_vq[1]); /* not urgent; do it later */
-       if ((features & VIRTIO_NET_F_CTRL_VQ)
-           && (features & VIRTIO_NET_F_CTRL_RX)) {
-               if (virtio_alloc_vq(vsc, &sc->sc_vq[2], 2,
-                                   NBPG, 1, "control") == 0) {
-                       sc->sc_vq[2].vq_done = vioif_ctrl_vq_done;
-                       cv_init(&sc->sc_ctrl_wait, "ctrl_vq");
-                       mutex_init(&sc->sc_ctrl_wait_lock,
-                                  MUTEX_DEFAULT, IPL_NET);
-                       sc->sc_ctrl_inuse = FREE;
-                       virtio_start_vq_intr(vsc, &sc->sc_vq[2]);
-                       vsc->sc_nvqs = 3;
+
+       if ((features & VIRTIO_NET_F_CTRL_VQ) &&
+           (features & VIRTIO_NET_F_CTRL_RX)) {
+               /*
+                * Allocating a virtqueue for control channel
+                */
+               r = virtio_alloc_vq(vsc, &sc->sc_vq[2], 2,
+                   NBPG, 1, "control");
+               if (r != 0) {
+                       aprint_error_dev(self, "failed to allocate "
+                           "a virtqueue for control channel\n");
+                       goto skip;
                }
+
+               sc->sc_vq[2].vq_done = vioif_ctrl_vq_done;
+               cv_init(&sc->sc_ctrl_wait, "ctrl_vq");
+               mutex_init(&sc->sc_ctrl_wait_lock, MUTEX_DEFAULT, IPL_NET);
+               sc->sc_ctrl_inuse = FREE;
+               virtio_start_vq_intr(vsc, &sc->sc_vq[2]);
+               vsc->sc_nvqs = 3;
        }
+skip:
 
 #ifdef VIOIF_MPSAFE
        flags = SOFTINT_NET | SOFTINT_MPSAFE;
diff -r f7cc68cd173d -r 6e3d7a301c41 sys/dev/pci/virtio.c
--- a/sys/dev/pci/virtio.c      Sun Oct 25 23:00:00 2015 +0000
+++ b/sys/dev/pci/virtio.c      Mon Oct 26 01:44:48 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: virtio.c,v 1.10 2015/10/15 02:40:38 ozaki-r Exp $      */
+/*     $NetBSD: virtio.c,v 1.11 2015/10/26 01:44:48 ozaki-r Exp $      */
 
 /*
  * Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.10 2015/10/15 02:40:38 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.11 2015/10/26 01:44:48 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -49,6 +49,15 @@
 static void    virtio_attach(device_t, device_t, void *);
 static int     virtio_detach(device_t, int);
 static int     virtio_intr(void *arg);
+static int     virtio_msix_queue_intr(void *);
+static int     virtio_msix_config_intr(void *);
+static int     virtio_setup_msix_vectors(struct virtio_softc *);
+static int     virtio_setup_msix_interrupts(struct virtio_softc *,
+                   struct pci_attach_args *);
+static int     virtio_setup_intx_interrupt(struct virtio_softc *,
+                   struct pci_attach_args *);
+static int     virtio_setup_interrupts(struct virtio_softc *,
+                   struct pci_attach_args *);
 static void    virtio_soft_intr(void *arg);
 static void    virtio_init_vq(struct virtio_softc *,
                    struct virtqueue *, const bool);
@@ -104,6 +113,203 @@
 };
 #define NDEVNAMES      (sizeof(virtio_device_name)/sizeof(char*))
 
+#define VIRTIO_MSIX_CONFIG_VECTOR_INDEX        0
+#define VIRTIO_MSIX_QUEUE_VECTOR_INDEX 1
+
+static int
+virtio_setup_msix_vectors(struct virtio_softc *sc)
+{
+       int offset, vector, ret, qid;
+
+       offset = VIRTIO_CONFIG_MSI_CONFIG_VECTOR;
+       vector = VIRTIO_MSIX_CONFIG_VECTOR_INDEX;
+
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, vector);
+       ret = bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset);
+       aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n",
+           vector, ret);
+       if (ret != vector)
+               return -1;
+
+       for (qid = 0; qid < sc->sc_nvqs; qid++) {
+               offset = VIRTIO_CONFIG_QUEUE_SELECT;
+               bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, qid);
+
+               offset = VIRTIO_CONFIG_MSI_QUEUE_VECTOR;
+               vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX;
+
+               bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, vector);
+               ret = bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset);
+               aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n",
+                   vector, ret);
+               if (ret != vector)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int
+virtio_setup_msix_interrupts(struct virtio_softc *sc,
+    struct pci_attach_args *pa)
+{
+       device_t self = sc->sc_dev;
+       pci_chipset_tag_t pc = pa->pa_pc;
+       char intrbuf[PCI_INTRSTR_LEN];
+       char const *intrstr;
+       int idx;
+
+       idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX;
+       if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE)
+               pci_intr_setattr(pc, &sc->sc_ihp[idx], PCI_INTR_MPSAFE, true);
+
+       sc->sc_ihs[idx] = pci_intr_establish_xname(pc, sc->sc_ihp[idx], IPL_NET,
+           virtio_msix_config_intr, sc, device_xname(sc->sc_dev));
+       if (sc->sc_ihs[idx] == NULL) {
+               aprint_error_dev(self, "couldn't establish MSI-X for config\n");
+               goto error;
+       }
+
+       idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX;
+       if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE)
+               pci_intr_setattr(pc, &sc->sc_ihp[idx], PCI_INTR_MPSAFE, true);
+
+       sc->sc_ihs[idx] = pci_intr_establish_xname(pc, sc->sc_ihp[idx], IPL_NET,
+           virtio_msix_queue_intr, sc, device_xname(sc->sc_dev));
+       if (sc->sc_ihs[idx] == NULL) {
+               aprint_error_dev(self, "couldn't establish MSI-X for queues\n");
+               goto error;
+       }
+
+       if (virtio_setup_msix_vectors(sc) != 0) {
+               aprint_error_dev(self, "couldn't setup MSI-X vectors\n");
+               goto error;
+       }
+
+       idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX;
+       intrstr = pci_intr_string(pc, sc->sc_ihp[idx], intrbuf, sizeof(intrbuf));
+       aprint_normal_dev(self, "config interrupting at %s\n", intrstr);
+       idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX;
+       intrstr = pci_intr_string(pc, sc->sc_ihp[idx], intrbuf, sizeof(intrbuf));
+       aprint_normal_dev(self, "queues interrupting at %s\n", intrstr);
+
+       return 0;
+
+error:
+       idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX;
+       if (sc->sc_ihs[idx] != NULL)
+               pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[idx]);
+       idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX;
+       if (sc->sc_ihs[idx] != NULL)
+               pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[idx]);
+
+       return -1;
+}
+
+static int
+virtio_setup_intx_interrupt(struct virtio_softc *sc, struct pci_attach_args *pa)
+{
+       device_t self = sc->sc_dev;
+       pci_chipset_tag_t pc = pa->pa_pc;
+       char intrbuf[PCI_INTRSTR_LEN];
+       char const *intrstr;
+
+       if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE)
+               pci_intr_setattr(pc, &sc->sc_ihp[0], PCI_INTR_MPSAFE, true);
+
+       sc->sc_ihs[0] = pci_intr_establish_xname(pc, sc->sc_ihp[0],
+           IPL_NET, virtio_intr, sc, device_xname(sc->sc_dev));
+       if (sc->sc_ihs[0] == NULL) {
+               aprint_error_dev(self, "couldn't establish INTx\n");
+               return -1;
+       }
+
+       intrstr = pci_intr_string(pc, sc->sc_ihp[0], intrbuf, sizeof(intrbuf));
+       aprint_normal_dev(self, "interrupting at %s\n", intrstr);
+
+       return 0;
+}
+
+static int
+virtio_setup_interrupts(struct virtio_softc *sc, struct pci_attach_args *pa)
+{
+       device_t self = sc->sc_dev;
+       pci_chipset_tag_t pc = pa->pa_pc;
+       int error;
+       int nmsix;
+       int counts[PCI_INTR_TYPE_SIZE];
+       pci_intr_type_t max_type;
+
+       nmsix = pci_msix_count(pa->pa_pc, pa->pa_tag);
+       aprint_debug_dev(self, "pci_msix_count=%d\n", nmsix);
+
+       /* We need at least two: one for config and the other for queues */
+       if ((sc->sc_flags & VIRTIO_F_PCI_INTR_MSIX) == 0 || nmsix < 2) {
+               /* Try INTx only */
+               max_type = PCI_INTR_TYPE_INTX;
+               counts[PCI_INTR_TYPE_INTX] = 1;
+       } else {
+               /* Try MSI-X first and INTx second */
+               max_type = PCI_INTR_TYPE_MSIX;
+               counts[PCI_INTR_TYPE_MSIX] = 2;
+               counts[PCI_INTR_TYPE_MSI] = 0;
+               counts[PCI_INTR_TYPE_INTX] = 1;
+       }
+



Home | Main Index | Thread Index | Old Index