110b59a9bSPeter Grehan /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 49da9560cSBryan Venteicher * Copyright (c) 2017, Bryan Venteicher <bryanv@FreeBSD.org> 510b59a9bSPeter Grehan * All rights reserved. 610b59a9bSPeter Grehan * 710b59a9bSPeter Grehan * Redistribution and use in source and binary forms, with or without 810b59a9bSPeter Grehan * modification, are permitted provided that the following conditions 910b59a9bSPeter Grehan * are met: 1010b59a9bSPeter Grehan * 1. Redistributions of source code must retain the above copyright 1110b59a9bSPeter Grehan * notice unmodified, this list of conditions, and the following 1210b59a9bSPeter Grehan * disclaimer. 1310b59a9bSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 1410b59a9bSPeter Grehan * notice, this list of conditions and the following disclaimer in the 1510b59a9bSPeter Grehan * documentation and/or other materials provided with the distribution. 1610b59a9bSPeter Grehan * 1710b59a9bSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1810b59a9bSPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1910b59a9bSPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2010b59a9bSPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2110b59a9bSPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2210b59a9bSPeter Grehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2310b59a9bSPeter Grehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2410b59a9bSPeter Grehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2510b59a9bSPeter Grehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2610b59a9bSPeter Grehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2710b59a9bSPeter Grehan */ 2810b59a9bSPeter Grehan 2910b59a9bSPeter Grehan #include <sys/cdefs.h> 3010b59a9bSPeter Grehan #include <sys/param.h> 3110b59a9bSPeter Grehan #include <sys/systm.h> 3210b59a9bSPeter Grehan #include <sys/bus.h> 3310b59a9bSPeter Grehan #include <sys/kernel.h> 34703f17d6SBryan Venteicher #include <sys/sbuf.h> 35703f17d6SBryan Venteicher #include <sys/sysctl.h> 3610b59a9bSPeter Grehan #include <sys/module.h> 3710b59a9bSPeter Grehan #include <sys/malloc.h> 3810b59a9bSPeter Grehan 3910b59a9bSPeter Grehan #include <machine/bus.h> 4010b59a9bSPeter Grehan #include <machine/resource.h> 4110b59a9bSPeter Grehan #include <sys/bus.h> 4210b59a9bSPeter Grehan #include <sys/rman.h> 4310b59a9bSPeter Grehan 4410b59a9bSPeter Grehan #include <dev/pci/pcivar.h> 4510b59a9bSPeter Grehan #include <dev/pci/pcireg.h> 4610b59a9bSPeter Grehan 4710b59a9bSPeter Grehan #include <dev/virtio/virtio.h> 4810b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 4910b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 509da9560cSBryan Venteicher #include <dev/virtio/pci/virtio_pci_var.h> 5110b59a9bSPeter Grehan 529da9560cSBryan Venteicher #include "virtio_pci_if.h" 5310b59a9bSPeter Grehan #include "virtio_if.h" 5410b59a9bSPeter Grehan 559da9560cSBryan Venteicher static void vtpci_describe_features(struct vtpci_common *, const char *, 5610b59a9bSPeter Grehan uint64_t); 579da9560cSBryan Venteicher static int vtpci_alloc_msix(struct vtpci_common *, int); 589da9560cSBryan Venteicher static int vtpci_alloc_msi(struct vtpci_common *); 599da9560cSBryan Venteicher static int vtpci_alloc_intr_msix_pervq(struct vtpci_common *); 609da9560cSBryan Venteicher static int vtpci_alloc_intr_msix_shared(struct vtpci_common *); 619da9560cSBryan Venteicher static int vtpci_alloc_intr_msi(struct vtpci_common *); 629da9560cSBryan Venteicher static int vtpci_alloc_intr_intx(struct vtpci_common *); 639da9560cSBryan Venteicher static int vtpci_alloc_interrupt(struct vtpci_common *, int, int, 6462a69c41SBryan Venteicher struct vtpci_interrupt *); 659da9560cSBryan Venteicher static void vtpci_free_interrupt(struct vtpci_common *, 6662a69c41SBryan Venteicher struct vtpci_interrupt *); 6710b59a9bSPeter Grehan 689da9560cSBryan Venteicher static void vtpci_free_interrupts(struct vtpci_common *); 699da9560cSBryan Venteicher static void vtpci_free_virtqueues(struct vtpci_common *); 709da9560cSBryan Venteicher static void vtpci_cleanup_setup_intr_attempt(struct vtpci_common *); 719da9560cSBryan Venteicher static int vtpci_alloc_intr_resources(struct vtpci_common *); 729da9560cSBryan Venteicher static int vtpci_setup_intx_interrupt(struct vtpci_common *, 739da9560cSBryan Venteicher enum intr_type); 749da9560cSBryan Venteicher static int vtpci_setup_pervq_msix_interrupts(struct vtpci_common *, 759da9560cSBryan Venteicher enum intr_type); 769da9560cSBryan Venteicher static int vtpci_set_host_msix_vectors(struct vtpci_common *); 779da9560cSBryan Venteicher static int vtpci_setup_msix_interrupts(struct vtpci_common *, 789da9560cSBryan Venteicher enum intr_type); 799da9560cSBryan Venteicher static int vtpci_setup_intrs(struct vtpci_common *, enum intr_type); 809da9560cSBryan Venteicher static int vtpci_reinit_virtqueue(struct vtpci_common *, int); 819da9560cSBryan Venteicher static void vtpci_intx_intr(void *); 826632efe4SBryan Venteicher static int vtpci_vq_shared_intr_filter(void *); 836632efe4SBryan Venteicher static void vtpci_vq_shared_intr(void *); 846632efe4SBryan Venteicher static int vtpci_vq_intr_filter(void *); 856632efe4SBryan Venteicher static void vtpci_vq_intr(void *); 866632efe4SBryan Venteicher static void vtpci_config_intr(void *); 8710b59a9bSPeter Grehan 88703f17d6SBryan Venteicher static void vtpci_setup_sysctl(struct vtpci_common *); 89703f17d6SBryan Venteicher 909da9560cSBryan Venteicher #define vtpci_setup_msi_interrupt vtpci_setup_intx_interrupt 91e026de11SBryan Venteicher 9210b59a9bSPeter Grehan /* 939da9560cSBryan Venteicher * This module contains two drivers: 949da9560cSBryan Venteicher * - virtio_pci_legacy for pre-V1 support 959da9560cSBryan Venteicher * - virtio_pci_modern for V1 support 9610b59a9bSPeter Grehan */ 9710b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 9810b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 9910b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 10010b59a9bSPeter Grehan 1019da9560cSBryan Venteicher int vtpci_disable_msix = 0; 1029da9560cSBryan Venteicher TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 1039da9560cSBryan Venteicher 1049da9560cSBryan Venteicher static uint8_t 1059da9560cSBryan Venteicher vtpci_read_isr(struct vtpci_common *cn) 10610b59a9bSPeter Grehan { 1079da9560cSBryan Venteicher return (VIRTIO_PCI_READ_ISR(cn->vtpci_dev)); 1089da9560cSBryan Venteicher } 10910b59a9bSPeter Grehan 1109da9560cSBryan Venteicher static uint16_t 1119da9560cSBryan Venteicher vtpci_get_vq_size(struct vtpci_common *cn, int idx) 1129da9560cSBryan Venteicher { 1139da9560cSBryan Venteicher return (VIRTIO_PCI_GET_VQ_SIZE(cn->vtpci_dev, idx)); 1149da9560cSBryan Venteicher } 11510b59a9bSPeter Grehan 1169da9560cSBryan Venteicher static bus_size_t 1179da9560cSBryan Venteicher vtpci_get_vq_notify_off(struct vtpci_common *cn, int idx) 1189da9560cSBryan Venteicher { 1199da9560cSBryan Venteicher return (VIRTIO_PCI_GET_VQ_NOTIFY_OFF(cn->vtpci_dev, idx)); 1209da9560cSBryan Venteicher } 12110b59a9bSPeter Grehan 1229da9560cSBryan Venteicher static void 1239da9560cSBryan Venteicher vtpci_set_vq(struct vtpci_common *cn, struct virtqueue *vq) 1249da9560cSBryan Venteicher { 1259da9560cSBryan Venteicher VIRTIO_PCI_SET_VQ(cn->vtpci_dev, vq); 1269da9560cSBryan Venteicher } 12710b59a9bSPeter Grehan 1289da9560cSBryan Venteicher static void 1299da9560cSBryan Venteicher vtpci_disable_vq(struct vtpci_common *cn, int idx) 1309da9560cSBryan Venteicher { 1319da9560cSBryan Venteicher VIRTIO_PCI_DISABLE_VQ(cn->vtpci_dev, idx); 13210b59a9bSPeter Grehan } 13310b59a9bSPeter Grehan 13410b59a9bSPeter Grehan static int 1359da9560cSBryan Venteicher vtpci_register_cfg_msix(struct vtpci_common *cn, struct vtpci_interrupt *intr) 13610b59a9bSPeter Grehan { 1379da9560cSBryan Venteicher return (VIRTIO_PCI_REGISTER_CFG_MSIX(cn->vtpci_dev, intr)); 1389da9560cSBryan Venteicher } 13910b59a9bSPeter Grehan 1409da9560cSBryan Venteicher static int 1419da9560cSBryan Venteicher vtpci_register_vq_msix(struct vtpci_common *cn, int idx, 1429da9560cSBryan Venteicher struct vtpci_interrupt *intr) 1439da9560cSBryan Venteicher { 1449da9560cSBryan Venteicher return (VIRTIO_PCI_REGISTER_VQ_MSIX(cn->vtpci_dev, idx, intr)); 1459da9560cSBryan Venteicher } 1469da9560cSBryan Venteicher 1479da9560cSBryan Venteicher void 1489da9560cSBryan Venteicher vtpci_init(struct vtpci_common *cn, device_t dev, bool modern) 1499da9560cSBryan Venteicher { 1509da9560cSBryan Venteicher 1519da9560cSBryan Venteicher cn->vtpci_dev = dev; 15210b59a9bSPeter Grehan 15310b59a9bSPeter Grehan pci_enable_busmaster(dev); 15410b59a9bSPeter Grehan 1559da9560cSBryan Venteicher if (modern) 1569da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MODERN; 157cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 1589da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_NO_MSI; 1599da9560cSBryan Venteicher if (pci_find_cap(dev, PCIY_MSIX, NULL) != 0) 1609da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_NO_MSIX; 161703f17d6SBryan Venteicher 162703f17d6SBryan Venteicher vtpci_setup_sysctl(cn); 16310b59a9bSPeter Grehan } 16410b59a9bSPeter Grehan 1659da9560cSBryan Venteicher int 1669da9560cSBryan Venteicher vtpci_add_child(struct vtpci_common *cn) 1679da9560cSBryan Venteicher { 1689da9560cSBryan Venteicher device_t dev, child; 16910b59a9bSPeter Grehan 1709da9560cSBryan Venteicher dev = cn->vtpci_dev; 17110b59a9bSPeter Grehan 1729da9560cSBryan Venteicher child = device_add_child(dev, NULL, -1); 1739da9560cSBryan Venteicher if (child == NULL) { 17410b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 17510b59a9bSPeter Grehan return (ENOMEM); 17610b59a9bSPeter Grehan } 17710b59a9bSPeter Grehan 1789da9560cSBryan Venteicher cn->vtpci_child_dev = child; 17910b59a9bSPeter Grehan 18010b59a9bSPeter Grehan return (0); 18110b59a9bSPeter Grehan } 18210b59a9bSPeter Grehan 1839da9560cSBryan Venteicher int 1849da9560cSBryan Venteicher vtpci_delete_child(struct vtpci_common *cn) 18510b59a9bSPeter Grehan { 1869da9560cSBryan Venteicher device_t dev, child; 18710b59a9bSPeter Grehan int error; 18810b59a9bSPeter Grehan 1899da9560cSBryan Venteicher dev = cn->vtpci_dev; 19010b59a9bSPeter Grehan 1919da9560cSBryan Venteicher child = cn->vtpci_child_dev; 1929da9560cSBryan Venteicher if (child != NULL) { 19310b59a9bSPeter Grehan error = device_delete_child(dev, child); 19410b59a9bSPeter Grehan if (error) 19510b59a9bSPeter Grehan return (error); 1969da9560cSBryan Venteicher cn->vtpci_child_dev = NULL; 19710b59a9bSPeter Grehan } 19810b59a9bSPeter Grehan 19910b59a9bSPeter Grehan return (0); 20010b59a9bSPeter Grehan } 20110b59a9bSPeter Grehan 2029da9560cSBryan Venteicher void 2039da9560cSBryan Venteicher vtpci_child_detached(struct vtpci_common *cn) 20410b59a9bSPeter Grehan { 20510b59a9bSPeter Grehan 2069da9560cSBryan Venteicher vtpci_release_child_resources(cn); 2079da9560cSBryan Venteicher 2089da9560cSBryan Venteicher cn->vtpci_child_feat_desc = NULL; 209703f17d6SBryan Venteicher cn->vtpci_host_features = 0; 2109da9560cSBryan Venteicher cn->vtpci_features = 0; 21110b59a9bSPeter Grehan } 21210b59a9bSPeter Grehan 2139da9560cSBryan Venteicher int 2149da9560cSBryan Venteicher vtpci_reinit(struct vtpci_common *cn) 21510b59a9bSPeter Grehan { 2169da9560cSBryan Venteicher int idx, error; 21710b59a9bSPeter Grehan 2189da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 2199da9560cSBryan Venteicher error = vtpci_reinit_virtqueue(cn, idx); 2209da9560cSBryan Venteicher if (error) 2219da9560cSBryan Venteicher return (error); 22210b59a9bSPeter Grehan } 22310b59a9bSPeter Grehan 2249da9560cSBryan Venteicher if (vtpci_is_msix_enabled(cn)) { 2259da9560cSBryan Venteicher error = vtpci_set_host_msix_vectors(cn); 2269da9560cSBryan Venteicher if (error) 2279da9560cSBryan Venteicher return (error); 2289da9560cSBryan Venteicher } 22910b59a9bSPeter Grehan 23010b59a9bSPeter Grehan return (0); 23110b59a9bSPeter Grehan } 23210b59a9bSPeter Grehan 23310b59a9bSPeter Grehan static void 2349da9560cSBryan Venteicher vtpci_describe_features(struct vtpci_common *cn, const char *msg, 2359da9560cSBryan Venteicher uint64_t features) 23610b59a9bSPeter Grehan { 2379da9560cSBryan Venteicher device_t dev, child; 23810b59a9bSPeter Grehan 2399da9560cSBryan Venteicher dev = cn->vtpci_dev; 2409da9560cSBryan Venteicher child = cn->vtpci_child_dev; 24110b59a9bSPeter Grehan 2429da9560cSBryan Venteicher if (device_is_attached(child) || bootverbose == 0) 2439da9560cSBryan Venteicher return; 2449da9560cSBryan Venteicher 2459da9560cSBryan Venteicher virtio_describe(dev, msg, features, cn->vtpci_child_feat_desc); 24610b59a9bSPeter Grehan } 24710b59a9bSPeter Grehan 2489da9560cSBryan Venteicher uint64_t 2499da9560cSBryan Venteicher vtpci_negotiate_features(struct vtpci_common *cn, 2509da9560cSBryan Venteicher uint64_t child_features, uint64_t host_features) 25110b59a9bSPeter Grehan { 2529da9560cSBryan Venteicher uint64_t features; 25310b59a9bSPeter Grehan 254703f17d6SBryan Venteicher cn->vtpci_host_features = host_features; 2559da9560cSBryan Venteicher vtpci_describe_features(cn, "host", host_features); 25610b59a9bSPeter Grehan 2579da9560cSBryan Venteicher /* 2589da9560cSBryan Venteicher * Limit negotiated features to what the driver, virtqueue, and 2599da9560cSBryan Venteicher * host all support. 2609da9560cSBryan Venteicher */ 2619da9560cSBryan Venteicher features = host_features & child_features; 2629da9560cSBryan Venteicher features = virtio_filter_transport_features(features); 2639da9560cSBryan Venteicher 2649da9560cSBryan Venteicher cn->vtpci_features = features; 265703f17d6SBryan Venteicher vtpci_describe_features(cn, "negotiated", features); 2669da9560cSBryan Venteicher 2679da9560cSBryan Venteicher return (features); 26810b59a9bSPeter Grehan } 26910b59a9bSPeter Grehan 270ccb576a8SMina Galić bool 2719da9560cSBryan Venteicher vtpci_with_feature(struct vtpci_common *cn, uint64_t feature) 27210b59a9bSPeter Grehan { 2739da9560cSBryan Venteicher return ((cn->vtpci_features & feature) != 0); 2749da9560cSBryan Venteicher } 27510b59a9bSPeter Grehan 2769da9560cSBryan Venteicher int 2779da9560cSBryan Venteicher vtpci_read_ivar(struct vtpci_common *cn, int index, uintptr_t *result) 2789da9560cSBryan Venteicher { 2799da9560cSBryan Venteicher device_t dev; 2809da9560cSBryan Venteicher int error; 28110b59a9bSPeter Grehan 2829da9560cSBryan Venteicher dev = cn->vtpci_dev; 2839da9560cSBryan Venteicher error = 0; 28410b59a9bSPeter Grehan 28510b59a9bSPeter Grehan switch (index) { 286310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 287310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 288310dacd0SPeter Grehan break; 289310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 290310dacd0SPeter Grehan *result = pci_get_vendor(dev); 291310dacd0SPeter Grehan break; 292310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 293310dacd0SPeter Grehan *result = pci_get_device(dev); 294310dacd0SPeter Grehan break; 295310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 2967cc8e55bSConrad Meyer *result = pci_get_subvendor(dev); 29710b59a9bSPeter Grehan break; 2989da9560cSBryan Venteicher case VIRTIO_IVAR_MODERN: 2999da9560cSBryan Venteicher *result = vtpci_is_modern(cn); 3009da9560cSBryan Venteicher break; 30110b59a9bSPeter Grehan default: 3029da9560cSBryan Venteicher error = ENOENT; 30310b59a9bSPeter Grehan } 30410b59a9bSPeter Grehan 3059da9560cSBryan Venteicher return (error); 30610b59a9bSPeter Grehan } 30710b59a9bSPeter Grehan 3089da9560cSBryan Venteicher int 3099da9560cSBryan Venteicher vtpci_write_ivar(struct vtpci_common *cn, int index, uintptr_t value) 31010b59a9bSPeter Grehan { 3119da9560cSBryan Venteicher int error; 31210b59a9bSPeter Grehan 3139da9560cSBryan Venteicher error = 0; 31410b59a9bSPeter Grehan 31510b59a9bSPeter Grehan switch (index) { 31610b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 3179da9560cSBryan Venteicher cn->vtpci_child_feat_desc = (void *) value; 31810b59a9bSPeter Grehan break; 31910b59a9bSPeter Grehan default: 3209da9560cSBryan Venteicher error = ENOENT; 32110b59a9bSPeter Grehan } 32210b59a9bSPeter Grehan 3239da9560cSBryan Venteicher return (error); 32410b59a9bSPeter Grehan } 32510b59a9bSPeter Grehan 3269da9560cSBryan Venteicher int 327*180c0240SMina Galić vtpci_alloc_virtqueues(struct vtpci_common *cn, int nvqs, 32810b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 32910b59a9bSPeter Grehan { 3309da9560cSBryan Venteicher device_t dev; 3319da9560cSBryan Venteicher int idx, align, error; 33210b59a9bSPeter Grehan 3339da9560cSBryan Venteicher dev = cn->vtpci_dev; 33410b59a9bSPeter Grehan 3359da9560cSBryan Venteicher /* 3369da9560cSBryan Venteicher * This is VIRTIO_PCI_VRING_ALIGN from legacy VirtIO. In modern VirtIO, 3379da9560cSBryan Venteicher * the tables do not have to be allocated contiguously, but we do so 3389da9560cSBryan Venteicher * anyways. 3399da9560cSBryan Venteicher */ 3409da9560cSBryan Venteicher align = 4096; 3419da9560cSBryan Venteicher 3429da9560cSBryan Venteicher if (cn->vtpci_nvqs != 0) 343310dacd0SPeter Grehan return (EALREADY); 34462a69c41SBryan Venteicher if (nvqs <= 0) 34510b59a9bSPeter Grehan return (EINVAL); 34610b59a9bSPeter Grehan 3479da9560cSBryan Venteicher cn->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 34862a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 3499da9560cSBryan Venteicher if (cn->vtpci_vqs == NULL) 35062a69c41SBryan Venteicher return (ENOMEM); 35162a69c41SBryan Venteicher 352310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 3539da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 3549da9560cSBryan Venteicher struct vq_alloc_info *info; 3559da9560cSBryan Venteicher struct virtqueue *vq; 3569da9560cSBryan Venteicher bus_size_t notify_offset; 3579da9560cSBryan Venteicher uint16_t size; 3589da9560cSBryan Venteicher 3599da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 360310dacd0SPeter Grehan info = &vq_info[idx]; 361310dacd0SPeter Grehan 3629da9560cSBryan Venteicher size = vtpci_get_vq_size(cn, idx); 3639da9560cSBryan Venteicher notify_offset = vtpci_get_vq_notify_off(cn, idx); 364310dacd0SPeter Grehan 3659da9560cSBryan Venteicher error = virtqueue_alloc(dev, idx, size, notify_offset, align, 366776d3d59SKristof Provost ~(vm_paddr_t)0, info, &vq); 36710b59a9bSPeter Grehan if (error) { 368310dacd0SPeter Grehan device_printf(dev, 369310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 370310dacd0SPeter Grehan break; 37110b59a9bSPeter Grehan } 37210b59a9bSPeter Grehan 3739da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 37410b59a9bSPeter Grehan 37562a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 37662a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 377310dacd0SPeter Grehan 3789da9560cSBryan Venteicher cn->vtpci_nvqs++; 37910b59a9bSPeter Grehan } 38010b59a9bSPeter Grehan 38162a69c41SBryan Venteicher if (error) 3829da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 38362a69c41SBryan Venteicher 384310dacd0SPeter Grehan return (error); 38510b59a9bSPeter Grehan } 38610b59a9bSPeter Grehan 38710b59a9bSPeter Grehan static int 3889da9560cSBryan Venteicher vtpci_alloc_msix(struct vtpci_common *cn, int nvectors) 38910b59a9bSPeter Grehan { 390310dacd0SPeter Grehan device_t dev; 391310dacd0SPeter Grehan int nmsix, cnt, required; 39210b59a9bSPeter Grehan 3939da9560cSBryan Venteicher dev = cn->vtpci_dev; 39410b59a9bSPeter Grehan 395310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 396310dacd0SPeter Grehan required = nvectors + 1; 39710b59a9bSPeter Grehan 398310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 399310dacd0SPeter Grehan if (nmsix < required) 400310dacd0SPeter Grehan return (1); 401310dacd0SPeter Grehan 402310dacd0SPeter Grehan cnt = required; 403310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 4049da9560cSBryan Venteicher cn->vtpci_nmsix_resources = required; 405310dacd0SPeter Grehan return (0); 40610b59a9bSPeter Grehan } 40710b59a9bSPeter Grehan 408310dacd0SPeter Grehan pci_release_msi(dev); 40910b59a9bSPeter Grehan 410310dacd0SPeter Grehan return (1); 41110b59a9bSPeter Grehan } 41210b59a9bSPeter Grehan 41310b59a9bSPeter Grehan static int 4149da9560cSBryan Venteicher vtpci_alloc_msi(struct vtpci_common *cn) 415310dacd0SPeter Grehan { 416310dacd0SPeter Grehan device_t dev; 417310dacd0SPeter Grehan int nmsi, cnt, required; 418310dacd0SPeter Grehan 4199da9560cSBryan Venteicher dev = cn->vtpci_dev; 420310dacd0SPeter Grehan required = 1; 421310dacd0SPeter Grehan 422310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 423310dacd0SPeter Grehan if (nmsi < required) 424310dacd0SPeter Grehan return (1); 425310dacd0SPeter Grehan 426310dacd0SPeter Grehan cnt = required; 42762a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 428310dacd0SPeter Grehan return (0); 429310dacd0SPeter Grehan 430310dacd0SPeter Grehan pci_release_msi(dev); 431310dacd0SPeter Grehan 432310dacd0SPeter Grehan return (1); 433310dacd0SPeter Grehan } 434310dacd0SPeter Grehan 435310dacd0SPeter Grehan static int 4369da9560cSBryan Venteicher vtpci_alloc_intr_msix_pervq(struct vtpci_common *cn) 437310dacd0SPeter Grehan { 438310dacd0SPeter Grehan int i, nvectors, error; 439310dacd0SPeter Grehan 4409da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 441310dacd0SPeter Grehan return (ENOTSUP); 442310dacd0SPeter Grehan 4439da9560cSBryan Venteicher for (nvectors = 0, i = 0; i < cn->vtpci_nvqs; i++) { 4449da9560cSBryan Venteicher if (cn->vtpci_vqs[i].vtv_no_intr == 0) 445310dacd0SPeter Grehan nvectors++; 446310dacd0SPeter Grehan } 447310dacd0SPeter Grehan 4489da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, nvectors); 449310dacd0SPeter Grehan if (error) 450310dacd0SPeter Grehan return (error); 451310dacd0SPeter Grehan 4529da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX; 453310dacd0SPeter Grehan 454310dacd0SPeter Grehan return (0); 455310dacd0SPeter Grehan } 456310dacd0SPeter Grehan 457310dacd0SPeter Grehan static int 4589da9560cSBryan Venteicher vtpci_alloc_intr_msix_shared(struct vtpci_common *cn) 459310dacd0SPeter Grehan { 460310dacd0SPeter Grehan int error; 461310dacd0SPeter Grehan 4629da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 463310dacd0SPeter Grehan return (ENOTSUP); 464310dacd0SPeter Grehan 4659da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, 1); 466310dacd0SPeter Grehan if (error) 467310dacd0SPeter Grehan return (error); 468310dacd0SPeter Grehan 4699da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 470310dacd0SPeter Grehan 471310dacd0SPeter Grehan return (0); 472310dacd0SPeter Grehan } 473310dacd0SPeter Grehan 474310dacd0SPeter Grehan static int 4759da9560cSBryan Venteicher vtpci_alloc_intr_msi(struct vtpci_common *cn) 476310dacd0SPeter Grehan { 477310dacd0SPeter Grehan int error; 478310dacd0SPeter Grehan 479310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 4809da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_NO_MSI) 481310dacd0SPeter Grehan return (ENOTSUP); 482310dacd0SPeter Grehan 4839da9560cSBryan Venteicher error = vtpci_alloc_msi(cn); 484310dacd0SPeter Grehan if (error) 485310dacd0SPeter Grehan return (error); 486310dacd0SPeter Grehan 4879da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSI; 488310dacd0SPeter Grehan 489310dacd0SPeter Grehan return (0); 490310dacd0SPeter Grehan } 491310dacd0SPeter Grehan 492310dacd0SPeter Grehan static int 4939da9560cSBryan Venteicher vtpci_alloc_intr_intx(struct vtpci_common *cn) 494310dacd0SPeter Grehan { 495310dacd0SPeter Grehan 4969da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_INTX; 49762a69c41SBryan Venteicher 49862a69c41SBryan Venteicher return (0); 49962a69c41SBryan Venteicher } 50062a69c41SBryan Venteicher 50162a69c41SBryan Venteicher static int 5029da9560cSBryan Venteicher vtpci_alloc_interrupt(struct vtpci_common *cn, int rid, int flags, 50362a69c41SBryan Venteicher struct vtpci_interrupt *intr) 50462a69c41SBryan Venteicher { 50562a69c41SBryan Venteicher struct resource *irq; 50662a69c41SBryan Venteicher 5079da9560cSBryan Venteicher irq = bus_alloc_resource_any(cn->vtpci_dev, SYS_RES_IRQ, &rid, flags); 50862a69c41SBryan Venteicher if (irq == NULL) 50962a69c41SBryan Venteicher return (ENXIO); 51062a69c41SBryan Venteicher 51162a69c41SBryan Venteicher intr->vti_irq = irq; 51262a69c41SBryan Venteicher intr->vti_rid = rid; 513310dacd0SPeter Grehan 514310dacd0SPeter Grehan return (0); 515310dacd0SPeter Grehan } 516310dacd0SPeter Grehan 51710b59a9bSPeter Grehan static void 5189da9560cSBryan Venteicher vtpci_free_interrupt(struct vtpci_common *cn, struct vtpci_interrupt *intr) 51910b59a9bSPeter Grehan { 52010b59a9bSPeter Grehan device_t dev; 52110b59a9bSPeter Grehan 5229da9560cSBryan Venteicher dev = cn->vtpci_dev; 52310b59a9bSPeter Grehan 52462a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 52562a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 52662a69c41SBryan Venteicher intr->vti_handler = NULL; 52710b59a9bSPeter Grehan } 52810b59a9bSPeter Grehan 52962a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 53062a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 53162a69c41SBryan Venteicher intr->vti_irq); 53262a69c41SBryan Venteicher intr->vti_irq = NULL; 53362a69c41SBryan Venteicher intr->vti_rid = -1; 53462a69c41SBryan Venteicher } 53510b59a9bSPeter Grehan } 53610b59a9bSPeter Grehan 53762a69c41SBryan Venteicher static void 5389da9560cSBryan Venteicher vtpci_free_interrupts(struct vtpci_common *cn) 53962a69c41SBryan Venteicher { 54062a69c41SBryan Venteicher struct vtpci_interrupt *intr; 54162a69c41SBryan Venteicher int i, nvq_intrs; 54262a69c41SBryan Venteicher 5439da9560cSBryan Venteicher vtpci_free_interrupt(cn, &cn->vtpci_device_interrupt); 54462a69c41SBryan Venteicher 5459da9560cSBryan Venteicher if (cn->vtpci_nmsix_resources != 0) { 5469da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 5479da9560cSBryan Venteicher cn->vtpci_nmsix_resources = 0; 54862a69c41SBryan Venteicher 5499da9560cSBryan Venteicher if ((intr = cn->vtpci_msix_vq_interrupts) != NULL) { 55062a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 5519da9560cSBryan Venteicher vtpci_free_interrupt(cn, intr); 55262a69c41SBryan Venteicher 5539da9560cSBryan Venteicher free(cn->vtpci_msix_vq_interrupts, M_DEVBUF); 5549da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = NULL; 55562a69c41SBryan Venteicher } 55610b59a9bSPeter Grehan } 557310dacd0SPeter Grehan 5589da9560cSBryan Venteicher if (cn->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 5599da9560cSBryan Venteicher pci_release_msi(cn->vtpci_dev); 560310dacd0SPeter Grehan 5619da9560cSBryan Venteicher cn->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 56210b59a9bSPeter Grehan } 56310b59a9bSPeter Grehan 56410b59a9bSPeter Grehan static void 5659da9560cSBryan Venteicher vtpci_free_virtqueues(struct vtpci_common *cn) 56610b59a9bSPeter Grehan { 56710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 56862a69c41SBryan Venteicher int idx; 56910b59a9bSPeter Grehan 5709da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 5719da9560cSBryan Venteicher vtpci_disable_vq(cn, idx); 57210b59a9bSPeter Grehan 5739da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 57462a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 57562a69c41SBryan Venteicher vqx->vtv_vq = NULL; 57610b59a9bSPeter Grehan } 577310dacd0SPeter Grehan 5789da9560cSBryan Venteicher free(cn->vtpci_vqs, M_DEVBUF); 5799da9560cSBryan Venteicher cn->vtpci_vqs = NULL; 5809da9560cSBryan Venteicher cn->vtpci_nvqs = 0; 58110b59a9bSPeter Grehan } 582310dacd0SPeter Grehan 5839da9560cSBryan Venteicher void 5849da9560cSBryan Venteicher vtpci_release_child_resources(struct vtpci_common *cn) 58562a69c41SBryan Venteicher { 58662a69c41SBryan Venteicher 5879da9560cSBryan Venteicher vtpci_free_interrupts(cn); 5889da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 58962a69c41SBryan Venteicher } 59062a69c41SBryan Venteicher 59162a69c41SBryan Venteicher static void 5929da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(struct vtpci_common *cn) 593310dacd0SPeter Grehan { 594310dacd0SPeter Grehan int idx; 595310dacd0SPeter Grehan 5969da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_MSIX) { 5979da9560cSBryan Venteicher vtpci_register_cfg_msix(cn, NULL); 598310dacd0SPeter Grehan 5999da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) 6009da9560cSBryan Venteicher vtpci_register_vq_msix(cn, idx, NULL); 601310dacd0SPeter Grehan } 602310dacd0SPeter Grehan 6039da9560cSBryan Venteicher vtpci_free_interrupts(cn); 60410b59a9bSPeter Grehan } 60510b59a9bSPeter Grehan 6069da9560cSBryan Venteicher static int 6079da9560cSBryan Venteicher vtpci_alloc_intr_resources(struct vtpci_common *cn) 60810b59a9bSPeter Grehan { 6099da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6109da9560cSBryan Venteicher int i, rid, flags, nvq_intrs, error; 6119da9560cSBryan Venteicher 6129da9560cSBryan Venteicher flags = RF_ACTIVE; 6139da9560cSBryan Venteicher 6149da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) { 6159da9560cSBryan Venteicher rid = 0; 6169da9560cSBryan Venteicher flags |= RF_SHAREABLE; 6179da9560cSBryan Venteicher } else 6189da9560cSBryan Venteicher rid = 1; 61910b59a9bSPeter Grehan 62010b59a9bSPeter Grehan /* 6219da9560cSBryan Venteicher * When using INTX or MSI interrupts, this resource handles all 6229da9560cSBryan Venteicher * interrupts. When using MSIX, this resource handles just the 6239da9560cSBryan Venteicher * configuration changed interrupt. 62410b59a9bSPeter Grehan */ 6259da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6269da9560cSBryan Venteicher 6279da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6289da9560cSBryan Venteicher if (error || cn->vtpci_flags & (VTPCI_FLAG_INTX | VTPCI_FLAG_MSI)) 6299da9560cSBryan Venteicher return (error); 6309da9560cSBryan Venteicher 6319da9560cSBryan Venteicher /* 6329da9560cSBryan Venteicher * Now allocate the interrupts for the virtqueues. This may be one 6339da9560cSBryan Venteicher * for all the virtqueues, or one for each virtqueue. Subtract one 6349da9560cSBryan Venteicher * below for because of the configuration changed interrupt. 6359da9560cSBryan Venteicher */ 6369da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 6379da9560cSBryan Venteicher 6389da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 6399da9560cSBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 6409da9560cSBryan Venteicher if (cn->vtpci_msix_vq_interrupts == NULL) 6419da9560cSBryan Venteicher return (ENOMEM); 6429da9560cSBryan Venteicher 6439da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6449da9560cSBryan Venteicher 6459da9560cSBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 6469da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6479da9560cSBryan Venteicher if (error) 6489da9560cSBryan Venteicher return (error); 6499da9560cSBryan Venteicher } 6509da9560cSBryan Venteicher 6519da9560cSBryan Venteicher return (0); 6529da9560cSBryan Venteicher } 6539da9560cSBryan Venteicher 6549da9560cSBryan Venteicher static int 6559da9560cSBryan Venteicher vtpci_setup_intx_interrupt(struct vtpci_common *cn, enum intr_type type) 6569da9560cSBryan Venteicher { 6579da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6589da9560cSBryan Venteicher int error; 6599da9560cSBryan Venteicher 6609da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6619da9560cSBryan Venteicher 6629da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 6639da9560cSBryan Venteicher vtpci_intx_intr, cn, &intr->vti_handler); 6649da9560cSBryan Venteicher 6659da9560cSBryan Venteicher return (error); 6669da9560cSBryan Venteicher } 6679da9560cSBryan Venteicher 6689da9560cSBryan Venteicher static int 6699da9560cSBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 6709da9560cSBryan Venteicher { 6719da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 6729da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6739da9560cSBryan Venteicher int i, error; 6749da9560cSBryan Venteicher 6759da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6769da9560cSBryan Venteicher 6779da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++) { 6789da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[i]; 6799da9560cSBryan Venteicher 6809da9560cSBryan Venteicher if (vqx->vtv_no_intr) 6819da9560cSBryan Venteicher continue; 6829da9560cSBryan Venteicher 6839da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 6849da9560cSBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 6859da9560cSBryan Venteicher &intr->vti_handler); 6869da9560cSBryan Venteicher if (error) 6879da9560cSBryan Venteicher return (error); 6889da9560cSBryan Venteicher 6899da9560cSBryan Venteicher intr++; 6909da9560cSBryan Venteicher } 6919da9560cSBryan Venteicher 6929da9560cSBryan Venteicher return (0); 6939da9560cSBryan Venteicher } 6949da9560cSBryan Venteicher 6959da9560cSBryan Venteicher static int 6969da9560cSBryan Venteicher vtpci_set_host_msix_vectors(struct vtpci_common *cn) 6979da9560cSBryan Venteicher { 6989da9560cSBryan Venteicher struct vtpci_interrupt *intr, *tintr; 6999da9560cSBryan Venteicher int idx, error; 7009da9560cSBryan Venteicher 7019da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 7029da9560cSBryan Venteicher error = vtpci_register_cfg_msix(cn, intr); 7039da9560cSBryan Venteicher if (error) 7049da9560cSBryan Venteicher return (error); 7059da9560cSBryan Venteicher 7069da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 7079da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 7089da9560cSBryan Venteicher if (cn->vtpci_vqs[idx].vtv_no_intr) 7099da9560cSBryan Venteicher tintr = NULL; 7109da9560cSBryan Venteicher else 7119da9560cSBryan Venteicher tintr = intr; 7129da9560cSBryan Venteicher 7139da9560cSBryan Venteicher error = vtpci_register_vq_msix(cn, idx, tintr); 7149da9560cSBryan Venteicher if (error) 7159da9560cSBryan Venteicher break; 7169da9560cSBryan Venteicher 7179da9560cSBryan Venteicher /* 7189da9560cSBryan Venteicher * For shared MSIX, all the virtqueues share the first 7199da9560cSBryan Venteicher * interrupt. 7209da9560cSBryan Venteicher */ 7219da9560cSBryan Venteicher if (!cn->vtpci_vqs[idx].vtv_no_intr && 7229da9560cSBryan Venteicher (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 7239da9560cSBryan Venteicher intr++; 7249da9560cSBryan Venteicher } 7259da9560cSBryan Venteicher 7269da9560cSBryan Venteicher return (error); 7279da9560cSBryan Venteicher } 7289da9560cSBryan Venteicher 7299da9560cSBryan Venteicher static int 7309da9560cSBryan Venteicher vtpci_setup_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 7319da9560cSBryan Venteicher { 7329da9560cSBryan Venteicher struct vtpci_interrupt *intr; 7339da9560cSBryan Venteicher int error; 7349da9560cSBryan Venteicher 7359da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 7369da9560cSBryan Venteicher 7379da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 7389da9560cSBryan Venteicher vtpci_config_intr, cn, &intr->vti_handler); 7399da9560cSBryan Venteicher if (error) 7409da9560cSBryan Venteicher return (error); 7419da9560cSBryan Venteicher 7429da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 7439da9560cSBryan Venteicher intr = &cn->vtpci_msix_vq_interrupts[0]; 7449da9560cSBryan Venteicher 7459da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 7469da9560cSBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, cn, 7479da9560cSBryan Venteicher &intr->vti_handler); 7489da9560cSBryan Venteicher } else 7499da9560cSBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(cn, type); 7509da9560cSBryan Venteicher 7519da9560cSBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(cn)); 7529da9560cSBryan Venteicher } 7539da9560cSBryan Venteicher 7549da9560cSBryan Venteicher static int 7559da9560cSBryan Venteicher vtpci_setup_intrs(struct vtpci_common *cn, enum intr_type type) 7569da9560cSBryan Venteicher { 7579da9560cSBryan Venteicher int error; 7589da9560cSBryan Venteicher 7599da9560cSBryan Venteicher type |= INTR_MPSAFE; 7609da9560cSBryan Venteicher KASSERT(cn->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 7619da9560cSBryan Venteicher ("%s: no interrupt type selected %#x", __func__, cn->vtpci_flags)); 7629da9560cSBryan Venteicher 7639da9560cSBryan Venteicher error = vtpci_alloc_intr_resources(cn); 7649da9560cSBryan Venteicher if (error) 7659da9560cSBryan Venteicher return (error); 7669da9560cSBryan Venteicher 7679da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 7689da9560cSBryan Venteicher error = vtpci_setup_intx_interrupt(cn, type); 7699da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 7709da9560cSBryan Venteicher error = vtpci_setup_msi_interrupt(cn, type); 7719da9560cSBryan Venteicher else 7729da9560cSBryan Venteicher error = vtpci_setup_msix_interrupts(cn, type); 7739da9560cSBryan Venteicher 7749da9560cSBryan Venteicher return (error); 7759da9560cSBryan Venteicher } 7769da9560cSBryan Venteicher 7779da9560cSBryan Venteicher int 7789da9560cSBryan Venteicher vtpci_setup_interrupts(struct vtpci_common *cn, enum intr_type type) 7799da9560cSBryan Venteicher { 7809da9560cSBryan Venteicher device_t dev; 7819da9560cSBryan Venteicher int attempt, error; 7829da9560cSBryan Venteicher 7839da9560cSBryan Venteicher dev = cn->vtpci_dev; 7849da9560cSBryan Venteicher 7859da9560cSBryan Venteicher for (attempt = 0; attempt < 5; attempt++) { 7869da9560cSBryan Venteicher /* 7879da9560cSBryan Venteicher * Start with the most desirable interrupt configuration and 7889da9560cSBryan Venteicher * fallback towards less desirable ones. 7899da9560cSBryan Venteicher */ 7909da9560cSBryan Venteicher switch (attempt) { 7919da9560cSBryan Venteicher case 0: 7929da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_pervq(cn); 7939da9560cSBryan Venteicher break; 7949da9560cSBryan Venteicher case 1: 7959da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_shared(cn); 7969da9560cSBryan Venteicher break; 7979da9560cSBryan Venteicher case 2: 7989da9560cSBryan Venteicher error = vtpci_alloc_intr_msi(cn); 7999da9560cSBryan Venteicher break; 8009da9560cSBryan Venteicher case 3: 8019da9560cSBryan Venteicher error = vtpci_alloc_intr_intx(cn); 8029da9560cSBryan Venteicher break; 8039da9560cSBryan Venteicher default: 8049da9560cSBryan Venteicher device_printf(dev, 8059da9560cSBryan Venteicher "exhausted all interrupt allocation attempts\n"); 8069da9560cSBryan Venteicher return (ENXIO); 8079da9560cSBryan Venteicher } 8089da9560cSBryan Venteicher 8099da9560cSBryan Venteicher if (error == 0 && vtpci_setup_intrs(cn, type) == 0) 8109da9560cSBryan Venteicher break; 8119da9560cSBryan Venteicher 8129da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(cn); 8139da9560cSBryan Venteicher } 8149da9560cSBryan Venteicher 8159da9560cSBryan Venteicher if (bootverbose) { 8169da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 8179da9560cSBryan Venteicher device_printf(dev, "using legacy interrupt\n"); 8189da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 8199da9560cSBryan Venteicher device_printf(dev, "using MSI interrupt\n"); 8209da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 8219da9560cSBryan Venteicher device_printf(dev, "using shared MSIX interrupts\n"); 8229da9560cSBryan Venteicher else 8239da9560cSBryan Venteicher device_printf(dev, "using per VQ MSIX interrupts\n"); 8249da9560cSBryan Venteicher } 8259da9560cSBryan Venteicher 8269da9560cSBryan Venteicher return (0); 8279da9560cSBryan Venteicher } 8289da9560cSBryan Venteicher 8299da9560cSBryan Venteicher static int 8309da9560cSBryan Venteicher vtpci_reinit_virtqueue(struct vtpci_common *cn, int idx) 8319da9560cSBryan Venteicher { 8329da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 8339da9560cSBryan Venteicher struct virtqueue *vq; 8349da9560cSBryan Venteicher int error; 8359da9560cSBryan Venteicher 8369da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 8379da9560cSBryan Venteicher vq = vqx->vtv_vq; 8389da9560cSBryan Venteicher 8399da9560cSBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 8409da9560cSBryan Venteicher 8419da9560cSBryan Venteicher error = virtqueue_reinit(vq, vtpci_get_vq_size(cn, idx)); 8429da9560cSBryan Venteicher if (error == 0) 8439da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 8449da9560cSBryan Venteicher 8459da9560cSBryan Venteicher return (error); 84610b59a9bSPeter Grehan } 84710b59a9bSPeter Grehan 848310dacd0SPeter Grehan static void 8499da9560cSBryan Venteicher vtpci_intx_intr(void *xcn) 850310dacd0SPeter Grehan { 8519da9560cSBryan Venteicher struct vtpci_common *cn; 85210b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 85310b59a9bSPeter Grehan int i; 85410b59a9bSPeter Grehan uint8_t isr; 85510b59a9bSPeter Grehan 8569da9560cSBryan Venteicher cn = xcn; 8579da9560cSBryan Venteicher isr = vtpci_read_isr(cn); 85810b59a9bSPeter Grehan 85910b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 8609da9560cSBryan Venteicher vtpci_config_intr(cn); 86110b59a9bSPeter Grehan 8626632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 8639da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8649da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 86562a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 86662a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 86762a69c41SBryan Venteicher } 8686632efe4SBryan Venteicher } 86910b59a9bSPeter Grehan } 87010b59a9bSPeter Grehan 87110b59a9bSPeter Grehan static int 8729da9560cSBryan Venteicher vtpci_vq_shared_intr_filter(void *xcn) 87310b59a9bSPeter Grehan { 8749da9560cSBryan Venteicher struct vtpci_common *cn; 87510b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 87610b59a9bSPeter Grehan int i, rc; 87710b59a9bSPeter Grehan 8789da9560cSBryan Venteicher cn = xcn; 8799da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 88010b59a9bSPeter Grehan rc = 0; 88110b59a9bSPeter Grehan 8829da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 88362a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 88462a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 88562a69c41SBryan Venteicher } 88610b59a9bSPeter Grehan 8876632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 8886632efe4SBryan Venteicher } 8896632efe4SBryan Venteicher 8906632efe4SBryan Venteicher static void 8919da9560cSBryan Venteicher vtpci_vq_shared_intr(void *xcn) 8926632efe4SBryan Venteicher { 8939da9560cSBryan Venteicher struct vtpci_common *cn; 8946632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 8956632efe4SBryan Venteicher int i; 8966632efe4SBryan Venteicher 8979da9560cSBryan Venteicher cn = xcn; 8989da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8996632efe4SBryan Venteicher 9009da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 90162a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 90262a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 90362a69c41SBryan Venteicher } 90410b59a9bSPeter Grehan } 90510b59a9bSPeter Grehan 90610b59a9bSPeter Grehan static int 9076632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 90810b59a9bSPeter Grehan { 90910b59a9bSPeter Grehan struct virtqueue *vq; 91010b59a9bSPeter Grehan int rc; 91110b59a9bSPeter Grehan 91210b59a9bSPeter Grehan vq = xvq; 9136632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 91410b59a9bSPeter Grehan 9156632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 91610b59a9bSPeter Grehan } 91710b59a9bSPeter Grehan 9186632efe4SBryan Venteicher static void 9196632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 9206632efe4SBryan Venteicher { 9216632efe4SBryan Venteicher struct virtqueue *vq; 9226632efe4SBryan Venteicher 9236632efe4SBryan Venteicher vq = xvq; 9246632efe4SBryan Venteicher virtqueue_intr(vq); 9256632efe4SBryan Venteicher } 9266632efe4SBryan Venteicher 9276632efe4SBryan Venteicher static void 9289da9560cSBryan Venteicher vtpci_config_intr(void *xcn) 92910b59a9bSPeter Grehan { 9309da9560cSBryan Venteicher struct vtpci_common *cn; 93110b59a9bSPeter Grehan device_t child; 93210b59a9bSPeter Grehan 9339da9560cSBryan Venteicher cn = xcn; 9349da9560cSBryan Venteicher child = cn->vtpci_child_dev; 93510b59a9bSPeter Grehan 93610b59a9bSPeter Grehan if (child != NULL) 9376632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 93810b59a9bSPeter Grehan } 939703f17d6SBryan Venteicher 940703f17d6SBryan Venteicher static int 941703f17d6SBryan Venteicher vtpci_feature_sysctl(struct sysctl_req *req, struct vtpci_common *cn, 942703f17d6SBryan Venteicher uint64_t features) 943703f17d6SBryan Venteicher { 944703f17d6SBryan Venteicher struct sbuf *sb; 945703f17d6SBryan Venteicher int error; 946703f17d6SBryan Venteicher 947703f17d6SBryan Venteicher sb = sbuf_new_for_sysctl(NULL, NULL, 256, req); 948703f17d6SBryan Venteicher if (sb == NULL) 949703f17d6SBryan Venteicher return (ENOMEM); 950703f17d6SBryan Venteicher 951703f17d6SBryan Venteicher error = virtio_describe_sbuf(sb, features, cn->vtpci_child_feat_desc); 952703f17d6SBryan Venteicher sbuf_delete(sb); 953703f17d6SBryan Venteicher 954703f17d6SBryan Venteicher return (error); 955703f17d6SBryan Venteicher } 956703f17d6SBryan Venteicher 957703f17d6SBryan Venteicher static int 958703f17d6SBryan Venteicher vtpci_host_features_sysctl(SYSCTL_HANDLER_ARGS) 959703f17d6SBryan Venteicher { 960703f17d6SBryan Venteicher struct vtpci_common *cn; 961703f17d6SBryan Venteicher 962703f17d6SBryan Venteicher cn = arg1; 963703f17d6SBryan Venteicher 964703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_host_features)); 965703f17d6SBryan Venteicher } 966703f17d6SBryan Venteicher 967703f17d6SBryan Venteicher static int 968703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl(SYSCTL_HANDLER_ARGS) 969703f17d6SBryan Venteicher { 970703f17d6SBryan Venteicher struct vtpci_common *cn; 971703f17d6SBryan Venteicher 972703f17d6SBryan Venteicher cn = arg1; 973703f17d6SBryan Venteicher 974703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_features)); 975703f17d6SBryan Venteicher } 976703f17d6SBryan Venteicher 977703f17d6SBryan Venteicher static void 978703f17d6SBryan Venteicher vtpci_setup_sysctl(struct vtpci_common *cn) 979703f17d6SBryan Venteicher { 980703f17d6SBryan Venteicher device_t dev; 981703f17d6SBryan Venteicher struct sysctl_ctx_list *ctx; 982703f17d6SBryan Venteicher struct sysctl_oid *tree; 983703f17d6SBryan Venteicher struct sysctl_oid_list *child; 984703f17d6SBryan Venteicher 985703f17d6SBryan Venteicher dev = cn->vtpci_dev; 986703f17d6SBryan Venteicher ctx = device_get_sysctl_ctx(dev); 987703f17d6SBryan Venteicher tree = device_get_sysctl_tree(dev); 988703f17d6SBryan Venteicher child = SYSCTL_CHILDREN(tree); 989703f17d6SBryan Venteicher 990703f17d6SBryan Venteicher SYSCTL_ADD_INT(ctx, child, OID_AUTO, "nvqs", 991703f17d6SBryan Venteicher CTLFLAG_RD, &cn->vtpci_nvqs, 0, "Number of virtqueues"); 992703f17d6SBryan Venteicher 993703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "host_features", 994703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 995703f17d6SBryan Venteicher vtpci_host_features_sysctl, "A", "Features supported by the host"); 996703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "negotiated_features", 997703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 998703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl, "A", "Features negotiated"); 999703f17d6SBryan Venteicher } 1000