Module Name: src Committed By: yamaguchi Date: Mon Jan 14 14:55:37 UTC 2019
Modified Files: src/sys/dev/pci: virtio.c virtio_pci.c virtiovar.h Log Message: Add multiqueue support, virtio(4) To generate a diff of this commit: cvs rdiff -u -r1.36 -r1.37 src/sys/dev/pci/virtio.c cvs rdiff -u -r1.5 -r1.6 src/sys/dev/pci/virtio_pci.c cvs rdiff -u -r1.12 -r1.13 src/sys/dev/pci/virtiovar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/virtio.c diff -u src/sys/dev/pci/virtio.c:1.36 src/sys/dev/pci/virtio.c:1.37 --- src/sys/dev/pci/virtio.c:1.36 Sun Sep 30 15:44:22 2018 +++ src/sys/dev/pci/virtio.c Mon Jan 14 14:55:37 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: virtio.c,v 1.36 2018/09/30 15:44:22 jmcneill Exp $ */ +/* $NetBSD: virtio.c,v 1.37 2019/01/14 14:55:37 yamaguchi Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.36 2018/09/30 15:44:22 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.37 2019/01/14 14:55:37 yamaguchi Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -235,6 +235,54 @@ vq_sync_indirect(struct virtio_softc *sc ops); } +static void +virtio_vq_soft_intr(void *arg) +{ + struct virtqueue *vq = arg; + + KASSERT(vq->vq_intrhand != NULL); + + (vq->vq_intrhand)(vq); +} + +static int +virtio_vq_softint_establish(struct virtio_softc *sc) +{ + struct virtqueue *vq; + int qid; + u_int flags; + + flags = SOFTINT_NET; + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + flags |= SOFTINT_MPSAFE; + + for (qid = 0; qid < sc->sc_nvqs; qid++) { + vq = &sc->sc_vqs[qid]; + vq->vq_soft_ih = + softint_establish(flags, virtio_vq_soft_intr, vq); + if (vq->vq_soft_ih == NULL) + return -1; + } + + return 0; +} + +static void +virtio_vq_softint_disestablish(struct virtio_softc *sc) +{ + struct virtqueue *vq; + int qid; + + for (qid = 0; qid < sc->sc_nvqs; qid++) { + vq = &sc->sc_vqs[qid]; + if (vq->vq_soft_ih == NULL) + continue; + + softint_disestablish(vq->vq_soft_ih); + vq->vq_soft_ih = NULL; + } +} + /* * Can be used as sc_intrhand. */ @@ -242,6 +290,26 @@ vq_sync_indirect(struct virtio_softc *sc * Scan vq, bus_dmamap_sync for the vqs (not for the payload), * and calls (*vq_done)() if some entries are consumed. */ +static int +virtio_vq_intr_common(struct virtqueue *vq) +{ + struct virtio_softc *sc = vq->vq_owner; + int r = 0; + + if (vq->vq_queued) { + vq->vq_queued = 0; + vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE); + } + vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); + membar_consumer(); + if (vq->vq_used_idx != vq->vq_used->idx) { + if (vq->vq_done) + r |= (vq->vq_done)(vq); + } + + return r; +} + int virtio_vq_intr(struct virtio_softc *sc) { @@ -250,21 +318,19 @@ virtio_vq_intr(struct virtio_softc *sc) for (i = 0; i < sc->sc_nvqs; i++) { vq = &sc->sc_vqs[i]; - if (vq->vq_queued) { - vq->vq_queued = 0; - vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE); - } - vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); - membar_consumer(); - if (vq->vq_used_idx != vq->vq_used->idx) { - if (vq->vq_done) - r |= (vq->vq_done)(vq); - } + r |= virtio_vq_intr_common(vq); } return r; } +static int +virtio_vq_mq_intr(struct virtqueue *vq) +{ + + return virtio_vq_intr_common(vq); +} + /* * Start/stop vq interrupt. No guarantee. */ @@ -409,6 +475,7 @@ virtio_alloc_vq(struct virtio_softc *sc, /* remember addresses and offsets for later use */ vq->vq_owner = sc; + vq->vq_intrhand = virtio_vq_mq_intr; vq->vq_num = vq_size; vq->vq_index = index; vq->vq_desc = vq->vq_vaddr; @@ -844,6 +911,16 @@ virtio_child_attach_start(struct virtio_ aprint_naive("\n"); } +void +virtio_child_attach_set_vqs(struct virtio_softc *sc, + struct virtqueue *vqs, int nvq_pairs) +{ + if (nvq_pairs > 1) + sc->sc_child_mq = true; + + sc->sc_vqs = vqs; +} + int virtio_child_attach_finish(struct virtio_softc *sc) { @@ -868,12 +945,26 @@ virtio_child_attach_finish(struct virtio "failed to establish soft interrupt\n"); goto fail; } + + if (sc->sc_child_mq) { + r = virtio_vq_softint_establish(sc); + aprint_error_dev(sc->sc_dev, + "failed to establish softint interrupt\n"); + goto fail; + } } virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); return 0; fail: + if (sc->sc_soft_ih) { + softint_disestablish(sc->sc_soft_ih); + sc->sc_soft_ih = NULL; + } + + virtio_vq_softint_disestablish(sc); + virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); return 1; } Index: src/sys/dev/pci/virtio_pci.c diff -u src/sys/dev/pci/virtio_pci.c:1.5 src/sys/dev/pci/virtio_pci.c:1.6 --- src/sys/dev/pci/virtio_pci.c:1.5 Wed Jun 6 16:11:36 2018 +++ src/sys/dev/pci/virtio_pci.c Mon Jan 14 14:55:37 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: virtio_pci.c,v 1.5 2018/06/06 16:11:36 jakllsch Exp $ */ +/* $NetBSD: virtio_pci.c,v 1.6 2019/01/14 14:55:37 yamaguchi Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -26,12 +26,13 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: virtio_pci.c,v 1.5 2018/06/06 16:11:36 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: virtio_pci.c,v 1.6 2019/01/14 14:55:37 yamaguchi Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/kmem.h> #include <sys/module.h> +#include <sys/interrupt.h> #include <sys/device.h> @@ -79,6 +80,7 @@ static void virtio_pci_free_interrupts(s static int virtio_pci_intr(void *arg); static int virtio_pci_msix_queue_intr(void *); +static int virtio_pci_msix_vq_intr(void *); static int virtio_pci_msix_config_intr(void *); static int virtio_pci_setup_msix_vectors(struct virtio_softc *); static int virtio_pci_setup_msix_interrupts(struct virtio_softc *, @@ -428,7 +430,7 @@ virtio_pci_setup_queue(struct virtio_sof if (psc->sc_ihs_num > 1) { int vec = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - if (false) /* (for per-vq vectors) */ + if (sc->sc_child_mq) vec += idx; bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, VIRTIO_CONFIG_MSI_QUEUE_VECTOR, vec); @@ -488,6 +490,9 @@ virtio_pci_setup_msix_vectors(struct vir offset = VIRTIO_CONFIG_MSI_QUEUE_VECTOR; vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + if (sc->sc_child_mq) + vector += qid; + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, offset, vector); ret = bus_space_read_stream_2(psc->sc_iot, psc->sc_ioh, offset); aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n", @@ -507,29 +512,57 @@ virtio_pci_setup_msix_interrupts(struct device_t self = sc->sc_dev; pci_chipset_tag_t pc = pa->pa_pc; char intrbuf[PCI_INTRSTR_LEN]; + char intr_xname[INTRDEVNAMEBUF]; char const *intrstr; - int idx; + int idx, qid, n; idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) pci_intr_setattr(pc, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); + snprintf(intr_xname, sizeof(intr_xname), "%s config", + device_xname(sc->sc_dev)); + psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], - sc->sc_ipl, virtio_pci_msix_config_intr, sc, device_xname(sc->sc_dev)); + sc->sc_ipl, virtio_pci_msix_config_intr, sc, intr_xname); if (psc->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, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); + if (sc->sc_child_mq) { + for (qid = 0; qid < sc->sc_nvqs; qid++) { + n = idx + qid; + + snprintf(intr_xname, sizeof(intr_xname), "%s vq#%d", + device_xname(sc->sc_dev), qid); + + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) { + pci_intr_setattr(pc, &psc->sc_ihp[n], + PCI_INTR_MPSAFE, true); + } + + psc->sc_ihs[n] = pci_intr_establish_xname(pc, psc->sc_ihp[n], + sc->sc_ipl, virtio_pci_msix_vq_intr, &sc->sc_vqs[qid], + intr_xname); + if (psc->sc_ihs[n] == NULL) { + aprint_error_dev(self, "couldn't establish MSI-X for a vq\n"); + goto error; + } + } + } else { + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + pci_intr_setattr(pc, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); - psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], - sc->sc_ipl, virtio_pci_msix_queue_intr, sc, device_xname(sc->sc_dev)); - if (psc->sc_ihs[idx] == NULL) { - aprint_error_dev(self, "couldn't establish MSI-X for queues\n"); - goto error; + snprintf(intr_xname, sizeof(intr_xname), "%s queues", + device_xname(sc->sc_dev)); + psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], + sc->sc_ipl, virtio_pci_msix_queue_intr, sc, intr_xname); + if (psc->sc_ihs[idx] == NULL) { + aprint_error_dev(self, "couldn't establish MSI-X for queues\n"); + goto error; + } } if (virtio_pci_setup_msix_vectors(sc) != 0) { @@ -541,8 +574,38 @@ virtio_pci_setup_msix_interrupts(struct intrstr = pci_intr_string(pc, psc->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, psc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); - aprint_normal_dev(self, "queues interrupting at %s\n", intrstr); + if (sc->sc_child_mq) { + kcpuset_t *affinity; + int affinity_to, r; + + kcpuset_create(&affinity, false); + + for (qid = 0; qid < sc->sc_nvqs; qid++) { + n = idx + qid; + affinity_to = (qid / 2) % ncpu; + + intrstr = pci_intr_string(pc, psc->sc_ihp[n], + intrbuf, sizeof(intrbuf)); + + kcpuset_zero(affinity); + kcpuset_set(affinity, affinity_to); + r = interrupt_distribute(psc->sc_ihs[n], affinity, NULL); + if (r == 0) { + aprint_normal_dev(self, + "for vq #%d interrupting at %s affinity to %u\n", + qid, intrstr, affinity_to); + } else { + aprint_normal_dev(self, + "for vq #%d interrupting at %s\n", + qid, intrstr); + } + } + + kcpuset_destroy(affinity); + } else { + intrstr = pci_intr_string(pc, psc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); + aprint_normal_dev(self, "queues interrupting at %s\n", intrstr); + } return 0; @@ -551,8 +614,18 @@ error: if (psc->sc_ihs[idx] != NULL) pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - if (psc->sc_ihs[idx] != NULL) - pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); + if (sc->sc_child_mq) { + for (qid = 0; qid < sc->sc_nvqs; qid++) { + n = idx + qid; + if (psc->sc_ihs[n] == NULL) + continue; + pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[n]); + } + + } else { + if (psc->sc_ihs[idx] != NULL) + pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); + } return -1; } @@ -604,8 +677,14 @@ virtio_pci_setup_interrupts(struct virti counts[PCI_INTR_TYPE_INTX] = 1; } else { /* Try MSI-X first and INTx second */ + if (sc->sc_child_mq && + sc->sc_nvqs > (nmsix - VIRTIO_MSIX_QUEUE_VECTOR_INDEX)) { + nmsix = 2; + sc->sc_child_mq = false; + } + max_type = PCI_INTR_TYPE_MSIX; - counts[PCI_INTR_TYPE_MSIX] = 2; + counts[PCI_INTR_TYPE_MSIX] = nmsix; counts[PCI_INTR_TYPE_MSI] = 0; counts[PCI_INTR_TYPE_INTX] = 1; } @@ -618,13 +697,13 @@ retry: } if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_MSIX) { - psc->sc_ihs = kmem_alloc(sizeof(*psc->sc_ihs) * 2, + psc->sc_ihs = kmem_alloc(sizeof(*psc->sc_ihs) * nmsix, KM_SLEEP); error = virtio_pci_setup_msix_interrupts(sc, &psc->sc_pa); if (error != 0) { - kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * 2); - pci_intr_release(pc, psc->sc_ihp, 2); + kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * nmsix); + pci_intr_release(pc, psc->sc_ihp, nmsix); /* Retry INTx */ max_type = PCI_INTR_TYPE_INTX; @@ -632,7 +711,7 @@ retry: goto retry; } - psc->sc_ihs_num = 2; + psc->sc_ihs_num = nmsix; psc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI; } else if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_INTX) { psc->sc_ihs = kmem_alloc(sizeof(*psc->sc_ihs) * 1, @@ -719,6 +798,22 @@ virtio_pci_msix_queue_intr(void *arg) } static int +virtio_pci_msix_vq_intr(void *arg) +{ + struct virtqueue *vq = arg; + int r = 0; + + if (vq->vq_intrhand != NULL) { + if (vq->vq_soft_ih) + softint_schedule(vq->vq_soft_ih); + else + r |= vq->vq_intrhand(vq); + } + + return r; +} + +static int virtio_pci_msix_config_intr(void *arg) { struct virtio_softc *sc = arg; Index: src/sys/dev/pci/virtiovar.h diff -u src/sys/dev/pci/virtiovar.h:1.12 src/sys/dev/pci/virtiovar.h:1.13 --- src/sys/dev/pci/virtiovar.h:1.12 Fri Jun 15 17:13:43 2018 +++ src/sys/dev/pci/virtiovar.h Mon Jan 14 14:55:37 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: virtiovar.h,v 1.12 2018/06/15 17:13:43 jakllsch Exp $ */ +/* $NetBSD: virtiovar.h,v 1.13 2019/01/14 14:55:37 yamaguchi Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -116,6 +116,9 @@ struct virtqueue { /* interrupt handler */ int (*vq_done)(struct virtqueue*); + void *vq_done_ctx; + void *vq_soft_ih; + int (*vq_intrhand)(struct virtqueue*); }; struct virtio_attach_args { @@ -161,6 +164,7 @@ struct virtio_softc { int sc_childdevid; device_t sc_child; /* set by child */ + bool sc_child_mq; virtio_callback sc_config_change; /* set by child */ virtio_callback sc_intrhand; /* set by child */ }; @@ -196,6 +200,8 @@ void virtio_child_attach_start(struct vi struct virtqueue *, virtio_callback, virtio_callback, int, int, const char *); +void virtio_child_attach_set_vqs(struct virtio_softc *, + struct virtqueue *, int); int virtio_child_attach_finish(struct virtio_softc *); void virtio_child_attach_failed(struct virtio_softc *); void virtio_child_detach(struct virtio_softc *);