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/param.h> 3010b59a9bSPeter Grehan #include <sys/systm.h> 3110b59a9bSPeter Grehan #include <sys/bus.h> 3210b59a9bSPeter Grehan #include <sys/kernel.h> 33703f17d6SBryan Venteicher #include <sys/sbuf.h> 34703f17d6SBryan Venteicher #include <sys/sysctl.h> 3510b59a9bSPeter Grehan #include <sys/module.h> 3610b59a9bSPeter Grehan #include <sys/malloc.h> 3710b59a9bSPeter Grehan 3810b59a9bSPeter Grehan #include <machine/bus.h> 3910b59a9bSPeter Grehan #include <machine/resource.h> 4010b59a9bSPeter Grehan #include <sys/bus.h> 4110b59a9bSPeter Grehan #include <sys/rman.h> 4210b59a9bSPeter Grehan 4310b59a9bSPeter Grehan #include <dev/pci/pcivar.h> 4410b59a9bSPeter Grehan #include <dev/pci/pcireg.h> 4510b59a9bSPeter Grehan 4610b59a9bSPeter Grehan #include <dev/virtio/virtio.h> 4710b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 4810b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 499da9560cSBryan Venteicher #include <dev/virtio/pci/virtio_pci_var.h> 5010b59a9bSPeter Grehan 519da9560cSBryan Venteicher #include "virtio_pci_if.h" 5210b59a9bSPeter Grehan #include "virtio_if.h" 5310b59a9bSPeter Grehan 549da9560cSBryan Venteicher static void vtpci_describe_features(struct vtpci_common *, const char *, 5510b59a9bSPeter Grehan uint64_t); 569da9560cSBryan Venteicher static int vtpci_alloc_msix(struct vtpci_common *, int); 579da9560cSBryan Venteicher static int vtpci_alloc_msi(struct vtpci_common *); 589da9560cSBryan Venteicher static int vtpci_alloc_intr_msix_pervq(struct vtpci_common *); 599da9560cSBryan Venteicher static int vtpci_alloc_intr_msix_shared(struct vtpci_common *); 609da9560cSBryan Venteicher static int vtpci_alloc_intr_msi(struct vtpci_common *); 619da9560cSBryan Venteicher static int vtpci_alloc_intr_intx(struct vtpci_common *); 629da9560cSBryan Venteicher static int vtpci_alloc_interrupt(struct vtpci_common *, int, int, 6362a69c41SBryan Venteicher struct vtpci_interrupt *); 649da9560cSBryan Venteicher static void vtpci_free_interrupt(struct vtpci_common *, 6562a69c41SBryan Venteicher struct vtpci_interrupt *); 6610b59a9bSPeter Grehan 679da9560cSBryan Venteicher static void vtpci_free_interrupts(struct vtpci_common *); 689da9560cSBryan Venteicher static void vtpci_free_virtqueues(struct vtpci_common *); 699da9560cSBryan Venteicher static void vtpci_cleanup_setup_intr_attempt(struct vtpci_common *); 709da9560cSBryan Venteicher static int vtpci_alloc_intr_resources(struct vtpci_common *); 719da9560cSBryan Venteicher static int vtpci_setup_intx_interrupt(struct vtpci_common *, 729da9560cSBryan Venteicher enum intr_type); 739da9560cSBryan Venteicher static int vtpci_setup_pervq_msix_interrupts(struct vtpci_common *, 749da9560cSBryan Venteicher enum intr_type); 759da9560cSBryan Venteicher static int vtpci_set_host_msix_vectors(struct vtpci_common *); 769da9560cSBryan Venteicher static int vtpci_setup_msix_interrupts(struct vtpci_common *, 779da9560cSBryan Venteicher enum intr_type); 789da9560cSBryan Venteicher static int vtpci_setup_intrs(struct vtpci_common *, enum intr_type); 799da9560cSBryan Venteicher static int vtpci_reinit_virtqueue(struct vtpci_common *, int); 809da9560cSBryan Venteicher static void vtpci_intx_intr(void *); 816632efe4SBryan Venteicher static int vtpci_vq_shared_intr_filter(void *); 826632efe4SBryan Venteicher static void vtpci_vq_shared_intr(void *); 836632efe4SBryan Venteicher static int vtpci_vq_intr_filter(void *); 846632efe4SBryan Venteicher static void vtpci_vq_intr(void *); 856632efe4SBryan Venteicher static void vtpci_config_intr(void *); 8610b59a9bSPeter Grehan 87703f17d6SBryan Venteicher static void vtpci_setup_sysctl(struct vtpci_common *); 88703f17d6SBryan Venteicher 899da9560cSBryan Venteicher #define vtpci_setup_msi_interrupt vtpci_setup_intx_interrupt 90e026de11SBryan Venteicher 9110b59a9bSPeter Grehan /* 929da9560cSBryan Venteicher * This module contains two drivers: 939da9560cSBryan Venteicher * - virtio_pci_legacy for pre-V1 support 949da9560cSBryan Venteicher * - virtio_pci_modern for V1 support 9510b59a9bSPeter Grehan */ 9610b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 9710b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 9810b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 9910b59a9bSPeter Grehan 1009da9560cSBryan Venteicher int vtpci_disable_msix = 0; 1019da9560cSBryan Venteicher TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 1029da9560cSBryan Venteicher 1039da9560cSBryan Venteicher static uint8_t 1049da9560cSBryan Venteicher vtpci_read_isr(struct vtpci_common *cn) 10510b59a9bSPeter Grehan { 1069da9560cSBryan Venteicher return (VIRTIO_PCI_READ_ISR(cn->vtpci_dev)); 1079da9560cSBryan Venteicher } 10810b59a9bSPeter Grehan 1099da9560cSBryan Venteicher static uint16_t 1109da9560cSBryan Venteicher vtpci_get_vq_size(struct vtpci_common *cn, int idx) 1119da9560cSBryan Venteicher { 1129da9560cSBryan Venteicher return (VIRTIO_PCI_GET_VQ_SIZE(cn->vtpci_dev, idx)); 1139da9560cSBryan Venteicher } 11410b59a9bSPeter Grehan 1159da9560cSBryan Venteicher static bus_size_t 1169da9560cSBryan Venteicher vtpci_get_vq_notify_off(struct vtpci_common *cn, int idx) 1179da9560cSBryan Venteicher { 1189da9560cSBryan Venteicher return (VIRTIO_PCI_GET_VQ_NOTIFY_OFF(cn->vtpci_dev, idx)); 1199da9560cSBryan Venteicher } 12010b59a9bSPeter Grehan 1219da9560cSBryan Venteicher static void 1229da9560cSBryan Venteicher vtpci_set_vq(struct vtpci_common *cn, struct virtqueue *vq) 1239da9560cSBryan Venteicher { 1249da9560cSBryan Venteicher VIRTIO_PCI_SET_VQ(cn->vtpci_dev, vq); 1259da9560cSBryan Venteicher } 12610b59a9bSPeter Grehan 1279da9560cSBryan Venteicher static void 1289da9560cSBryan Venteicher vtpci_disable_vq(struct vtpci_common *cn, int idx) 1299da9560cSBryan Venteicher { 1309da9560cSBryan Venteicher VIRTIO_PCI_DISABLE_VQ(cn->vtpci_dev, idx); 13110b59a9bSPeter Grehan } 13210b59a9bSPeter Grehan 13310b59a9bSPeter Grehan static int 1349da9560cSBryan Venteicher vtpci_register_cfg_msix(struct vtpci_common *cn, struct vtpci_interrupt *intr) 13510b59a9bSPeter Grehan { 1369da9560cSBryan Venteicher return (VIRTIO_PCI_REGISTER_CFG_MSIX(cn->vtpci_dev, intr)); 1379da9560cSBryan Venteicher } 13810b59a9bSPeter Grehan 1399da9560cSBryan Venteicher static int 1409da9560cSBryan Venteicher vtpci_register_vq_msix(struct vtpci_common *cn, int idx, 1419da9560cSBryan Venteicher struct vtpci_interrupt *intr) 1429da9560cSBryan Venteicher { 1439da9560cSBryan Venteicher return (VIRTIO_PCI_REGISTER_VQ_MSIX(cn->vtpci_dev, idx, intr)); 1449da9560cSBryan Venteicher } 1459da9560cSBryan Venteicher 1469da9560cSBryan Venteicher void 1479da9560cSBryan Venteicher vtpci_init(struct vtpci_common *cn, device_t dev, bool modern) 1489da9560cSBryan Venteicher { 1499da9560cSBryan Venteicher 1509da9560cSBryan Venteicher cn->vtpci_dev = dev; 15110b59a9bSPeter Grehan 15210b59a9bSPeter Grehan pci_enable_busmaster(dev); 15310b59a9bSPeter Grehan 1549da9560cSBryan Venteicher if (modern) 1559da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MODERN; 156cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 1579da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_NO_MSI; 1589da9560cSBryan Venteicher if (pci_find_cap(dev, PCIY_MSIX, NULL) != 0) 1599da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_NO_MSIX; 160703f17d6SBryan Venteicher 161703f17d6SBryan Venteicher vtpci_setup_sysctl(cn); 16210b59a9bSPeter Grehan } 16310b59a9bSPeter Grehan 1649da9560cSBryan Venteicher int 1659da9560cSBryan Venteicher vtpci_add_child(struct vtpci_common *cn) 1669da9560cSBryan Venteicher { 1679da9560cSBryan Venteicher device_t dev, child; 16810b59a9bSPeter Grehan 1699da9560cSBryan Venteicher dev = cn->vtpci_dev; 17010b59a9bSPeter Grehan 171*5b56413dSWarner Losh child = device_add_child(dev, NULL, DEVICE_UNIT_ANY); 1729da9560cSBryan Venteicher if (child == NULL) { 17310b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 17410b59a9bSPeter Grehan return (ENOMEM); 17510b59a9bSPeter Grehan } 17610b59a9bSPeter Grehan 1779da9560cSBryan Venteicher cn->vtpci_child_dev = child; 17810b59a9bSPeter Grehan 17910b59a9bSPeter Grehan return (0); 18010b59a9bSPeter Grehan } 18110b59a9bSPeter Grehan 1829da9560cSBryan Venteicher int 1839da9560cSBryan Venteicher vtpci_delete_child(struct vtpci_common *cn) 18410b59a9bSPeter Grehan { 1859da9560cSBryan Venteicher device_t dev, child; 18610b59a9bSPeter Grehan int error; 18710b59a9bSPeter Grehan 1889da9560cSBryan Venteicher dev = cn->vtpci_dev; 18910b59a9bSPeter Grehan 1909da9560cSBryan Venteicher child = cn->vtpci_child_dev; 1919da9560cSBryan Venteicher if (child != NULL) { 19210b59a9bSPeter Grehan error = device_delete_child(dev, child); 19310b59a9bSPeter Grehan if (error) 19410b59a9bSPeter Grehan return (error); 1959da9560cSBryan Venteicher cn->vtpci_child_dev = NULL; 19610b59a9bSPeter Grehan } 19710b59a9bSPeter Grehan 19810b59a9bSPeter Grehan return (0); 19910b59a9bSPeter Grehan } 20010b59a9bSPeter Grehan 2019da9560cSBryan Venteicher void 2029da9560cSBryan Venteicher vtpci_child_detached(struct vtpci_common *cn) 20310b59a9bSPeter Grehan { 20410b59a9bSPeter Grehan 2059da9560cSBryan Venteicher vtpci_release_child_resources(cn); 2069da9560cSBryan Venteicher 2079da9560cSBryan Venteicher cn->vtpci_child_feat_desc = NULL; 208703f17d6SBryan Venteicher cn->vtpci_host_features = 0; 2099da9560cSBryan Venteicher cn->vtpci_features = 0; 21010b59a9bSPeter Grehan } 21110b59a9bSPeter Grehan 2129da9560cSBryan Venteicher int 2139da9560cSBryan Venteicher vtpci_reinit(struct vtpci_common *cn) 21410b59a9bSPeter Grehan { 2159da9560cSBryan Venteicher int idx, error; 21610b59a9bSPeter Grehan 2179da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 2189da9560cSBryan Venteicher error = vtpci_reinit_virtqueue(cn, idx); 2199da9560cSBryan Venteicher if (error) 2209da9560cSBryan Venteicher return (error); 22110b59a9bSPeter Grehan } 22210b59a9bSPeter Grehan 2239da9560cSBryan Venteicher if (vtpci_is_msix_enabled(cn)) { 2249da9560cSBryan Venteicher error = vtpci_set_host_msix_vectors(cn); 2259da9560cSBryan Venteicher if (error) 2269da9560cSBryan Venteicher return (error); 2279da9560cSBryan Venteicher } 22810b59a9bSPeter Grehan 22910b59a9bSPeter Grehan return (0); 23010b59a9bSPeter Grehan } 23110b59a9bSPeter Grehan 23210b59a9bSPeter Grehan static void 2339da9560cSBryan Venteicher vtpci_describe_features(struct vtpci_common *cn, const char *msg, 2349da9560cSBryan Venteicher uint64_t features) 23510b59a9bSPeter Grehan { 2369da9560cSBryan Venteicher device_t dev, child; 23710b59a9bSPeter Grehan 2389da9560cSBryan Venteicher dev = cn->vtpci_dev; 2399da9560cSBryan Venteicher child = cn->vtpci_child_dev; 24010b59a9bSPeter Grehan 2419da9560cSBryan Venteicher if (device_is_attached(child) || bootverbose == 0) 2429da9560cSBryan Venteicher return; 2439da9560cSBryan Venteicher 2449da9560cSBryan Venteicher virtio_describe(dev, msg, features, cn->vtpci_child_feat_desc); 24510b59a9bSPeter Grehan } 24610b59a9bSPeter Grehan 2479da9560cSBryan Venteicher uint64_t 2489da9560cSBryan Venteicher vtpci_negotiate_features(struct vtpci_common *cn, 2499da9560cSBryan Venteicher uint64_t child_features, uint64_t host_features) 25010b59a9bSPeter Grehan { 2519da9560cSBryan Venteicher uint64_t features; 25210b59a9bSPeter Grehan 253703f17d6SBryan Venteicher cn->vtpci_host_features = host_features; 2549da9560cSBryan Venteicher vtpci_describe_features(cn, "host", host_features); 25510b59a9bSPeter Grehan 2569da9560cSBryan Venteicher /* 2579da9560cSBryan Venteicher * Limit negotiated features to what the driver, virtqueue, and 2589da9560cSBryan Venteicher * host all support. 2599da9560cSBryan Venteicher */ 2609da9560cSBryan Venteicher features = host_features & child_features; 2619da9560cSBryan Venteicher features = virtio_filter_transport_features(features); 2629da9560cSBryan Venteicher 2639da9560cSBryan Venteicher cn->vtpci_features = features; 264703f17d6SBryan Venteicher vtpci_describe_features(cn, "negotiated", features); 2659da9560cSBryan Venteicher 2669da9560cSBryan Venteicher return (features); 26710b59a9bSPeter Grehan } 26810b59a9bSPeter Grehan 269ccb576a8SMina Galić bool 2709da9560cSBryan Venteicher vtpci_with_feature(struct vtpci_common *cn, uint64_t feature) 27110b59a9bSPeter Grehan { 2729da9560cSBryan Venteicher return ((cn->vtpci_features & feature) != 0); 2739da9560cSBryan Venteicher } 27410b59a9bSPeter Grehan 2759da9560cSBryan Venteicher int 2769da9560cSBryan Venteicher vtpci_read_ivar(struct vtpci_common *cn, int index, uintptr_t *result) 2779da9560cSBryan Venteicher { 2789da9560cSBryan Venteicher device_t dev; 2799da9560cSBryan Venteicher int error; 28010b59a9bSPeter Grehan 2819da9560cSBryan Venteicher dev = cn->vtpci_dev; 2829da9560cSBryan Venteicher error = 0; 28310b59a9bSPeter Grehan 28410b59a9bSPeter Grehan switch (index) { 285310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 286310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 287310dacd0SPeter Grehan break; 288310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 289310dacd0SPeter Grehan *result = pci_get_vendor(dev); 290310dacd0SPeter Grehan break; 291310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 292310dacd0SPeter Grehan *result = pci_get_device(dev); 293310dacd0SPeter Grehan break; 294310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 2957cc8e55bSConrad Meyer *result = pci_get_subvendor(dev); 29610b59a9bSPeter Grehan break; 2979da9560cSBryan Venteicher case VIRTIO_IVAR_MODERN: 2989da9560cSBryan Venteicher *result = vtpci_is_modern(cn); 2999da9560cSBryan Venteicher break; 30010b59a9bSPeter Grehan default: 3019da9560cSBryan Venteicher error = ENOENT; 30210b59a9bSPeter Grehan } 30310b59a9bSPeter Grehan 3049da9560cSBryan Venteicher return (error); 30510b59a9bSPeter Grehan } 30610b59a9bSPeter Grehan 3079da9560cSBryan Venteicher int 3089da9560cSBryan Venteicher vtpci_write_ivar(struct vtpci_common *cn, int index, uintptr_t value) 30910b59a9bSPeter Grehan { 3109da9560cSBryan Venteicher int error; 31110b59a9bSPeter Grehan 3129da9560cSBryan Venteicher error = 0; 31310b59a9bSPeter Grehan 31410b59a9bSPeter Grehan switch (index) { 31510b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 3169da9560cSBryan Venteicher cn->vtpci_child_feat_desc = (void *) value; 31710b59a9bSPeter Grehan break; 31810b59a9bSPeter Grehan default: 3199da9560cSBryan Venteicher error = ENOENT; 32010b59a9bSPeter Grehan } 32110b59a9bSPeter Grehan 3229da9560cSBryan Venteicher return (error); 32310b59a9bSPeter Grehan } 32410b59a9bSPeter Grehan 3259da9560cSBryan Venteicher int 326180c0240SMina Galić vtpci_alloc_virtqueues(struct vtpci_common *cn, int nvqs, 32710b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 32810b59a9bSPeter Grehan { 3299da9560cSBryan Venteicher device_t dev; 3309da9560cSBryan Venteicher int idx, align, error; 33110b59a9bSPeter Grehan 3329da9560cSBryan Venteicher dev = cn->vtpci_dev; 33310b59a9bSPeter Grehan 3349da9560cSBryan Venteicher /* 3359da9560cSBryan Venteicher * This is VIRTIO_PCI_VRING_ALIGN from legacy VirtIO. In modern VirtIO, 3369da9560cSBryan Venteicher * the tables do not have to be allocated contiguously, but we do so 3379da9560cSBryan Venteicher * anyways. 3389da9560cSBryan Venteicher */ 3399da9560cSBryan Venteicher align = 4096; 3409da9560cSBryan Venteicher 3419da9560cSBryan Venteicher if (cn->vtpci_nvqs != 0) 342310dacd0SPeter Grehan return (EALREADY); 34362a69c41SBryan Venteicher if (nvqs <= 0) 34410b59a9bSPeter Grehan return (EINVAL); 34510b59a9bSPeter Grehan 3469da9560cSBryan Venteicher cn->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 34762a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 3489da9560cSBryan Venteicher if (cn->vtpci_vqs == NULL) 34962a69c41SBryan Venteicher return (ENOMEM); 35062a69c41SBryan Venteicher 351310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 3529da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 3539da9560cSBryan Venteicher struct vq_alloc_info *info; 3549da9560cSBryan Venteicher struct virtqueue *vq; 3559da9560cSBryan Venteicher bus_size_t notify_offset; 3569da9560cSBryan Venteicher uint16_t size; 3579da9560cSBryan Venteicher 3589da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 359310dacd0SPeter Grehan info = &vq_info[idx]; 360310dacd0SPeter Grehan 3619da9560cSBryan Venteicher size = vtpci_get_vq_size(cn, idx); 3629da9560cSBryan Venteicher notify_offset = vtpci_get_vq_notify_off(cn, idx); 363310dacd0SPeter Grehan 3649da9560cSBryan Venteicher error = virtqueue_alloc(dev, idx, size, notify_offset, align, 365776d3d59SKristof Provost ~(vm_paddr_t)0, info, &vq); 36610b59a9bSPeter Grehan if (error) { 367310dacd0SPeter Grehan device_printf(dev, 368310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 369310dacd0SPeter Grehan break; 37010b59a9bSPeter Grehan } 37110b59a9bSPeter Grehan 3729da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 37310b59a9bSPeter Grehan 37462a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 37562a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 376310dacd0SPeter Grehan 3779da9560cSBryan Venteicher cn->vtpci_nvqs++; 37810b59a9bSPeter Grehan } 37910b59a9bSPeter Grehan 38062a69c41SBryan Venteicher if (error) 3819da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 38262a69c41SBryan Venteicher 383310dacd0SPeter Grehan return (error); 38410b59a9bSPeter Grehan } 38510b59a9bSPeter Grehan 38610b59a9bSPeter Grehan static int 3879da9560cSBryan Venteicher vtpci_alloc_msix(struct vtpci_common *cn, int nvectors) 38810b59a9bSPeter Grehan { 389310dacd0SPeter Grehan device_t dev; 390310dacd0SPeter Grehan int nmsix, cnt, required; 39110b59a9bSPeter Grehan 3929da9560cSBryan Venteicher dev = cn->vtpci_dev; 39310b59a9bSPeter Grehan 394310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 395310dacd0SPeter Grehan required = nvectors + 1; 39610b59a9bSPeter Grehan 397310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 398310dacd0SPeter Grehan if (nmsix < required) 399310dacd0SPeter Grehan return (1); 400310dacd0SPeter Grehan 401310dacd0SPeter Grehan cnt = required; 402310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 4039da9560cSBryan Venteicher cn->vtpci_nmsix_resources = required; 404310dacd0SPeter Grehan return (0); 40510b59a9bSPeter Grehan } 40610b59a9bSPeter Grehan 407310dacd0SPeter Grehan pci_release_msi(dev); 40810b59a9bSPeter Grehan 409310dacd0SPeter Grehan return (1); 41010b59a9bSPeter Grehan } 41110b59a9bSPeter Grehan 41210b59a9bSPeter Grehan static int 4139da9560cSBryan Venteicher vtpci_alloc_msi(struct vtpci_common *cn) 414310dacd0SPeter Grehan { 415310dacd0SPeter Grehan device_t dev; 416310dacd0SPeter Grehan int nmsi, cnt, required; 417310dacd0SPeter Grehan 4189da9560cSBryan Venteicher dev = cn->vtpci_dev; 419310dacd0SPeter Grehan required = 1; 420310dacd0SPeter Grehan 421310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 422310dacd0SPeter Grehan if (nmsi < required) 423310dacd0SPeter Grehan return (1); 424310dacd0SPeter Grehan 425310dacd0SPeter Grehan cnt = required; 42662a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 427310dacd0SPeter Grehan return (0); 428310dacd0SPeter Grehan 429310dacd0SPeter Grehan pci_release_msi(dev); 430310dacd0SPeter Grehan 431310dacd0SPeter Grehan return (1); 432310dacd0SPeter Grehan } 433310dacd0SPeter Grehan 434310dacd0SPeter Grehan static int 4359da9560cSBryan Venteicher vtpci_alloc_intr_msix_pervq(struct vtpci_common *cn) 436310dacd0SPeter Grehan { 437310dacd0SPeter Grehan int i, nvectors, error; 438310dacd0SPeter Grehan 4399da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 440310dacd0SPeter Grehan return (ENOTSUP); 441310dacd0SPeter Grehan 4429da9560cSBryan Venteicher for (nvectors = 0, i = 0; i < cn->vtpci_nvqs; i++) { 4439da9560cSBryan Venteicher if (cn->vtpci_vqs[i].vtv_no_intr == 0) 444310dacd0SPeter Grehan nvectors++; 445310dacd0SPeter Grehan } 446310dacd0SPeter Grehan 4479da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, nvectors); 448310dacd0SPeter Grehan if (error) 449310dacd0SPeter Grehan return (error); 450310dacd0SPeter Grehan 4519da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX; 452310dacd0SPeter Grehan 453310dacd0SPeter Grehan return (0); 454310dacd0SPeter Grehan } 455310dacd0SPeter Grehan 456310dacd0SPeter Grehan static int 4579da9560cSBryan Venteicher vtpci_alloc_intr_msix_shared(struct vtpci_common *cn) 458310dacd0SPeter Grehan { 459310dacd0SPeter Grehan int error; 460310dacd0SPeter Grehan 4619da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 462310dacd0SPeter Grehan return (ENOTSUP); 463310dacd0SPeter Grehan 4649da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, 1); 465310dacd0SPeter Grehan if (error) 466310dacd0SPeter Grehan return (error); 467310dacd0SPeter Grehan 4689da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 469310dacd0SPeter Grehan 470310dacd0SPeter Grehan return (0); 471310dacd0SPeter Grehan } 472310dacd0SPeter Grehan 473310dacd0SPeter Grehan static int 4749da9560cSBryan Venteicher vtpci_alloc_intr_msi(struct vtpci_common *cn) 475310dacd0SPeter Grehan { 476310dacd0SPeter Grehan int error; 477310dacd0SPeter Grehan 478310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 4799da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_NO_MSI) 480310dacd0SPeter Grehan return (ENOTSUP); 481310dacd0SPeter Grehan 4829da9560cSBryan Venteicher error = vtpci_alloc_msi(cn); 483310dacd0SPeter Grehan if (error) 484310dacd0SPeter Grehan return (error); 485310dacd0SPeter Grehan 4869da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSI; 487310dacd0SPeter Grehan 488310dacd0SPeter Grehan return (0); 489310dacd0SPeter Grehan } 490310dacd0SPeter Grehan 491310dacd0SPeter Grehan static int 4929da9560cSBryan Venteicher vtpci_alloc_intr_intx(struct vtpci_common *cn) 493310dacd0SPeter Grehan { 494310dacd0SPeter Grehan 4959da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_INTX; 49662a69c41SBryan Venteicher 49762a69c41SBryan Venteicher return (0); 49862a69c41SBryan Venteicher } 49962a69c41SBryan Venteicher 50062a69c41SBryan Venteicher static int 5019da9560cSBryan Venteicher vtpci_alloc_interrupt(struct vtpci_common *cn, int rid, int flags, 50262a69c41SBryan Venteicher struct vtpci_interrupt *intr) 50362a69c41SBryan Venteicher { 50462a69c41SBryan Venteicher struct resource *irq; 50562a69c41SBryan Venteicher 5069da9560cSBryan Venteicher irq = bus_alloc_resource_any(cn->vtpci_dev, SYS_RES_IRQ, &rid, flags); 50762a69c41SBryan Venteicher if (irq == NULL) 50862a69c41SBryan Venteicher return (ENXIO); 50962a69c41SBryan Venteicher 51062a69c41SBryan Venteicher intr->vti_irq = irq; 51162a69c41SBryan Venteicher intr->vti_rid = rid; 512310dacd0SPeter Grehan 513310dacd0SPeter Grehan return (0); 514310dacd0SPeter Grehan } 515310dacd0SPeter Grehan 51610b59a9bSPeter Grehan static void 5179da9560cSBryan Venteicher vtpci_free_interrupt(struct vtpci_common *cn, struct vtpci_interrupt *intr) 51810b59a9bSPeter Grehan { 51910b59a9bSPeter Grehan device_t dev; 52010b59a9bSPeter Grehan 5219da9560cSBryan Venteicher dev = cn->vtpci_dev; 52210b59a9bSPeter Grehan 52362a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 52462a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 52562a69c41SBryan Venteicher intr->vti_handler = NULL; 52610b59a9bSPeter Grehan } 52710b59a9bSPeter Grehan 52862a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 52962a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 53062a69c41SBryan Venteicher intr->vti_irq); 53162a69c41SBryan Venteicher intr->vti_irq = NULL; 53262a69c41SBryan Venteicher intr->vti_rid = -1; 53362a69c41SBryan Venteicher } 53410b59a9bSPeter Grehan } 53510b59a9bSPeter Grehan 53662a69c41SBryan Venteicher static void 5379da9560cSBryan Venteicher vtpci_free_interrupts(struct vtpci_common *cn) 53862a69c41SBryan Venteicher { 53962a69c41SBryan Venteicher struct vtpci_interrupt *intr; 54062a69c41SBryan Venteicher int i, nvq_intrs; 54162a69c41SBryan Venteicher 5429da9560cSBryan Venteicher vtpci_free_interrupt(cn, &cn->vtpci_device_interrupt); 54362a69c41SBryan Venteicher 5449da9560cSBryan Venteicher if (cn->vtpci_nmsix_resources != 0) { 5459da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 5469da9560cSBryan Venteicher cn->vtpci_nmsix_resources = 0; 54762a69c41SBryan Venteicher 5489da9560cSBryan Venteicher if ((intr = cn->vtpci_msix_vq_interrupts) != NULL) { 54962a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 5509da9560cSBryan Venteicher vtpci_free_interrupt(cn, intr); 55162a69c41SBryan Venteicher 5529da9560cSBryan Venteicher free(cn->vtpci_msix_vq_interrupts, M_DEVBUF); 5539da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = NULL; 55462a69c41SBryan Venteicher } 55510b59a9bSPeter Grehan } 556310dacd0SPeter Grehan 5579da9560cSBryan Venteicher if (cn->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 5589da9560cSBryan Venteicher pci_release_msi(cn->vtpci_dev); 559310dacd0SPeter Grehan 5609da9560cSBryan Venteicher cn->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 56110b59a9bSPeter Grehan } 56210b59a9bSPeter Grehan 56310b59a9bSPeter Grehan static void 5649da9560cSBryan Venteicher vtpci_free_virtqueues(struct vtpci_common *cn) 56510b59a9bSPeter Grehan { 56610b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 56762a69c41SBryan Venteicher int idx; 56810b59a9bSPeter Grehan 5699da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 5709da9560cSBryan Venteicher vtpci_disable_vq(cn, idx); 57110b59a9bSPeter Grehan 5729da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 57362a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 57462a69c41SBryan Venteicher vqx->vtv_vq = NULL; 57510b59a9bSPeter Grehan } 576310dacd0SPeter Grehan 5779da9560cSBryan Venteicher free(cn->vtpci_vqs, M_DEVBUF); 5789da9560cSBryan Venteicher cn->vtpci_vqs = NULL; 5799da9560cSBryan Venteicher cn->vtpci_nvqs = 0; 58010b59a9bSPeter Grehan } 581310dacd0SPeter Grehan 5829da9560cSBryan Venteicher void 5839da9560cSBryan Venteicher vtpci_release_child_resources(struct vtpci_common *cn) 58462a69c41SBryan Venteicher { 58562a69c41SBryan Venteicher 5869da9560cSBryan Venteicher vtpci_free_interrupts(cn); 5879da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 58862a69c41SBryan Venteicher } 58962a69c41SBryan Venteicher 59062a69c41SBryan Venteicher static void 5919da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(struct vtpci_common *cn) 592310dacd0SPeter Grehan { 593310dacd0SPeter Grehan int idx; 594310dacd0SPeter Grehan 5959da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_MSIX) { 5969da9560cSBryan Venteicher vtpci_register_cfg_msix(cn, NULL); 597310dacd0SPeter Grehan 5989da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) 5999da9560cSBryan Venteicher vtpci_register_vq_msix(cn, idx, NULL); 600310dacd0SPeter Grehan } 601310dacd0SPeter Grehan 6029da9560cSBryan Venteicher vtpci_free_interrupts(cn); 60310b59a9bSPeter Grehan } 60410b59a9bSPeter Grehan 6059da9560cSBryan Venteicher static int 6069da9560cSBryan Venteicher vtpci_alloc_intr_resources(struct vtpci_common *cn) 60710b59a9bSPeter Grehan { 6089da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6099da9560cSBryan Venteicher int i, rid, flags, nvq_intrs, error; 6109da9560cSBryan Venteicher 6119da9560cSBryan Venteicher flags = RF_ACTIVE; 6129da9560cSBryan Venteicher 6139da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) { 6149da9560cSBryan Venteicher rid = 0; 6159da9560cSBryan Venteicher flags |= RF_SHAREABLE; 6169da9560cSBryan Venteicher } else 6179da9560cSBryan Venteicher rid = 1; 61810b59a9bSPeter Grehan 61910b59a9bSPeter Grehan /* 6209da9560cSBryan Venteicher * When using INTX or MSI interrupts, this resource handles all 6219da9560cSBryan Venteicher * interrupts. When using MSIX, this resource handles just the 6229da9560cSBryan Venteicher * configuration changed interrupt. 62310b59a9bSPeter Grehan */ 6249da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6259da9560cSBryan Venteicher 6269da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6279da9560cSBryan Venteicher if (error || cn->vtpci_flags & (VTPCI_FLAG_INTX | VTPCI_FLAG_MSI)) 6289da9560cSBryan Venteicher return (error); 6299da9560cSBryan Venteicher 6309da9560cSBryan Venteicher /* 6319da9560cSBryan Venteicher * Now allocate the interrupts for the virtqueues. This may be one 6329da9560cSBryan Venteicher * for all the virtqueues, or one for each virtqueue. Subtract one 6339da9560cSBryan Venteicher * below for because of the configuration changed interrupt. 6349da9560cSBryan Venteicher */ 6359da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 6369da9560cSBryan Venteicher 6379da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 6389da9560cSBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 6399da9560cSBryan Venteicher if (cn->vtpci_msix_vq_interrupts == NULL) 6409da9560cSBryan Venteicher return (ENOMEM); 6419da9560cSBryan Venteicher 6429da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6439da9560cSBryan Venteicher 6449da9560cSBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 6459da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6469da9560cSBryan Venteicher if (error) 6479da9560cSBryan Venteicher return (error); 6489da9560cSBryan Venteicher } 6499da9560cSBryan Venteicher 6509da9560cSBryan Venteicher return (0); 6519da9560cSBryan Venteicher } 6529da9560cSBryan Venteicher 6539da9560cSBryan Venteicher static int 6549da9560cSBryan Venteicher vtpci_setup_intx_interrupt(struct vtpci_common *cn, enum intr_type type) 6559da9560cSBryan Venteicher { 6569da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6579da9560cSBryan Venteicher int error; 6589da9560cSBryan Venteicher 6599da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6609da9560cSBryan Venteicher 6619da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 6629da9560cSBryan Venteicher vtpci_intx_intr, cn, &intr->vti_handler); 6639da9560cSBryan Venteicher 6649da9560cSBryan Venteicher return (error); 6659da9560cSBryan Venteicher } 6669da9560cSBryan Venteicher 6679da9560cSBryan Venteicher static int 6689da9560cSBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 6699da9560cSBryan Venteicher { 6709da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 6719da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6729da9560cSBryan Venteicher int i, error; 6739da9560cSBryan Venteicher 6749da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6759da9560cSBryan Venteicher 6769da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++) { 6779da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[i]; 6789da9560cSBryan Venteicher 6799da9560cSBryan Venteicher if (vqx->vtv_no_intr) 6809da9560cSBryan Venteicher continue; 6819da9560cSBryan Venteicher 6829da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 6839da9560cSBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 6849da9560cSBryan Venteicher &intr->vti_handler); 6859da9560cSBryan Venteicher if (error) 6869da9560cSBryan Venteicher return (error); 6879da9560cSBryan Venteicher 6889da9560cSBryan Venteicher intr++; 6899da9560cSBryan Venteicher } 6909da9560cSBryan Venteicher 6919da9560cSBryan Venteicher return (0); 6929da9560cSBryan Venteicher } 6939da9560cSBryan Venteicher 6949da9560cSBryan Venteicher static int 6959da9560cSBryan Venteicher vtpci_set_host_msix_vectors(struct vtpci_common *cn) 6969da9560cSBryan Venteicher { 6979da9560cSBryan Venteicher struct vtpci_interrupt *intr, *tintr; 6989da9560cSBryan Venteicher int idx, error; 6999da9560cSBryan Venteicher 7009da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 7019da9560cSBryan Venteicher error = vtpci_register_cfg_msix(cn, intr); 7029da9560cSBryan Venteicher if (error) 7039da9560cSBryan Venteicher return (error); 7049da9560cSBryan Venteicher 7059da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 7069da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 7079da9560cSBryan Venteicher if (cn->vtpci_vqs[idx].vtv_no_intr) 7089da9560cSBryan Venteicher tintr = NULL; 7099da9560cSBryan Venteicher else 7109da9560cSBryan Venteicher tintr = intr; 7119da9560cSBryan Venteicher 7129da9560cSBryan Venteicher error = vtpci_register_vq_msix(cn, idx, tintr); 7139da9560cSBryan Venteicher if (error) 7149da9560cSBryan Venteicher break; 7159da9560cSBryan Venteicher 7169da9560cSBryan Venteicher /* 7179da9560cSBryan Venteicher * For shared MSIX, all the virtqueues share the first 7189da9560cSBryan Venteicher * interrupt. 7199da9560cSBryan Venteicher */ 7209da9560cSBryan Venteicher if (!cn->vtpci_vqs[idx].vtv_no_intr && 7219da9560cSBryan Venteicher (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 7229da9560cSBryan Venteicher intr++; 7239da9560cSBryan Venteicher } 7249da9560cSBryan Venteicher 7259da9560cSBryan Venteicher return (error); 7269da9560cSBryan Venteicher } 7279da9560cSBryan Venteicher 7289da9560cSBryan Venteicher static int 7299da9560cSBryan Venteicher vtpci_setup_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 7309da9560cSBryan Venteicher { 7319da9560cSBryan Venteicher struct vtpci_interrupt *intr; 7329da9560cSBryan Venteicher int error; 7339da9560cSBryan Venteicher 7349da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 7359da9560cSBryan Venteicher 7369da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 7379da9560cSBryan Venteicher vtpci_config_intr, cn, &intr->vti_handler); 7389da9560cSBryan Venteicher if (error) 7399da9560cSBryan Venteicher return (error); 7409da9560cSBryan Venteicher 7419da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 7429da9560cSBryan Venteicher intr = &cn->vtpci_msix_vq_interrupts[0]; 7439da9560cSBryan Venteicher 7449da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 7459da9560cSBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, cn, 7469da9560cSBryan Venteicher &intr->vti_handler); 7479da9560cSBryan Venteicher } else 7489da9560cSBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(cn, type); 7499da9560cSBryan Venteicher 7509da9560cSBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(cn)); 7519da9560cSBryan Venteicher } 7529da9560cSBryan Venteicher 7539da9560cSBryan Venteicher static int 7549da9560cSBryan Venteicher vtpci_setup_intrs(struct vtpci_common *cn, enum intr_type type) 7559da9560cSBryan Venteicher { 7569da9560cSBryan Venteicher int error; 7579da9560cSBryan Venteicher 7589da9560cSBryan Venteicher type |= INTR_MPSAFE; 7599da9560cSBryan Venteicher KASSERT(cn->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 7609da9560cSBryan Venteicher ("%s: no interrupt type selected %#x", __func__, cn->vtpci_flags)); 7619da9560cSBryan Venteicher 7629da9560cSBryan Venteicher error = vtpci_alloc_intr_resources(cn); 7639da9560cSBryan Venteicher if (error) 7649da9560cSBryan Venteicher return (error); 7659da9560cSBryan Venteicher 7669da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 7679da9560cSBryan Venteicher error = vtpci_setup_intx_interrupt(cn, type); 7689da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 7699da9560cSBryan Venteicher error = vtpci_setup_msi_interrupt(cn, type); 7709da9560cSBryan Venteicher else 7719da9560cSBryan Venteicher error = vtpci_setup_msix_interrupts(cn, type); 7729da9560cSBryan Venteicher 7739da9560cSBryan Venteicher return (error); 7749da9560cSBryan Venteicher } 7759da9560cSBryan Venteicher 7769da9560cSBryan Venteicher int 7779da9560cSBryan Venteicher vtpci_setup_interrupts(struct vtpci_common *cn, enum intr_type type) 7789da9560cSBryan Venteicher { 7799da9560cSBryan Venteicher device_t dev; 7809da9560cSBryan Venteicher int attempt, error; 7819da9560cSBryan Venteicher 7829da9560cSBryan Venteicher dev = cn->vtpci_dev; 7839da9560cSBryan Venteicher 7849da9560cSBryan Venteicher for (attempt = 0; attempt < 5; attempt++) { 7859da9560cSBryan Venteicher /* 7869da9560cSBryan Venteicher * Start with the most desirable interrupt configuration and 7879da9560cSBryan Venteicher * fallback towards less desirable ones. 7889da9560cSBryan Venteicher */ 7899da9560cSBryan Venteicher switch (attempt) { 7909da9560cSBryan Venteicher case 0: 7919da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_pervq(cn); 7929da9560cSBryan Venteicher break; 7939da9560cSBryan Venteicher case 1: 7949da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_shared(cn); 7959da9560cSBryan Venteicher break; 7969da9560cSBryan Venteicher case 2: 7979da9560cSBryan Venteicher error = vtpci_alloc_intr_msi(cn); 7989da9560cSBryan Venteicher break; 7999da9560cSBryan Venteicher case 3: 8009da9560cSBryan Venteicher error = vtpci_alloc_intr_intx(cn); 8019da9560cSBryan Venteicher break; 8029da9560cSBryan Venteicher default: 8039da9560cSBryan Venteicher device_printf(dev, 8049da9560cSBryan Venteicher "exhausted all interrupt allocation attempts\n"); 8059da9560cSBryan Venteicher return (ENXIO); 8069da9560cSBryan Venteicher } 8079da9560cSBryan Venteicher 8089da9560cSBryan Venteicher if (error == 0 && vtpci_setup_intrs(cn, type) == 0) 8099da9560cSBryan Venteicher break; 8109da9560cSBryan Venteicher 8119da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(cn); 8129da9560cSBryan Venteicher } 8139da9560cSBryan Venteicher 8149da9560cSBryan Venteicher if (bootverbose) { 8159da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 8169da9560cSBryan Venteicher device_printf(dev, "using legacy interrupt\n"); 8179da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 8189da9560cSBryan Venteicher device_printf(dev, "using MSI interrupt\n"); 8199da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 8209da9560cSBryan Venteicher device_printf(dev, "using shared MSIX interrupts\n"); 8219da9560cSBryan Venteicher else 8229da9560cSBryan Venteicher device_printf(dev, "using per VQ MSIX interrupts\n"); 8239da9560cSBryan Venteicher } 8249da9560cSBryan Venteicher 8259da9560cSBryan Venteicher return (0); 8269da9560cSBryan Venteicher } 8279da9560cSBryan Venteicher 8289da9560cSBryan Venteicher static int 8299da9560cSBryan Venteicher vtpci_reinit_virtqueue(struct vtpci_common *cn, int idx) 8309da9560cSBryan Venteicher { 8319da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 8329da9560cSBryan Venteicher struct virtqueue *vq; 8339da9560cSBryan Venteicher int error; 8349da9560cSBryan Venteicher 8359da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 8369da9560cSBryan Venteicher vq = vqx->vtv_vq; 8379da9560cSBryan Venteicher 8389da9560cSBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 8399da9560cSBryan Venteicher 8409da9560cSBryan Venteicher error = virtqueue_reinit(vq, vtpci_get_vq_size(cn, idx)); 8419da9560cSBryan Venteicher if (error == 0) 8429da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 8439da9560cSBryan Venteicher 8449da9560cSBryan Venteicher return (error); 84510b59a9bSPeter Grehan } 84610b59a9bSPeter Grehan 847310dacd0SPeter Grehan static void 8489da9560cSBryan Venteicher vtpci_intx_intr(void *xcn) 849310dacd0SPeter Grehan { 8509da9560cSBryan Venteicher struct vtpci_common *cn; 85110b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 85210b59a9bSPeter Grehan int i; 85310b59a9bSPeter Grehan uint8_t isr; 85410b59a9bSPeter Grehan 8559da9560cSBryan Venteicher cn = xcn; 8569da9560cSBryan Venteicher isr = vtpci_read_isr(cn); 85710b59a9bSPeter Grehan 85810b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 8599da9560cSBryan Venteicher vtpci_config_intr(cn); 86010b59a9bSPeter Grehan 8616632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 8629da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8639da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 86462a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 86562a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 86662a69c41SBryan Venteicher } 8676632efe4SBryan Venteicher } 86810b59a9bSPeter Grehan } 86910b59a9bSPeter Grehan 87010b59a9bSPeter Grehan static int 8719da9560cSBryan Venteicher vtpci_vq_shared_intr_filter(void *xcn) 87210b59a9bSPeter Grehan { 8739da9560cSBryan Venteicher struct vtpci_common *cn; 87410b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 87510b59a9bSPeter Grehan int i, rc; 87610b59a9bSPeter Grehan 8779da9560cSBryan Venteicher cn = xcn; 8789da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 87910b59a9bSPeter Grehan rc = 0; 88010b59a9bSPeter Grehan 8819da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 88262a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 88362a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 88462a69c41SBryan Venteicher } 88510b59a9bSPeter Grehan 8866632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 8876632efe4SBryan Venteicher } 8886632efe4SBryan Venteicher 8896632efe4SBryan Venteicher static void 8909da9560cSBryan Venteicher vtpci_vq_shared_intr(void *xcn) 8916632efe4SBryan Venteicher { 8929da9560cSBryan Venteicher struct vtpci_common *cn; 8936632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 8946632efe4SBryan Venteicher int i; 8956632efe4SBryan Venteicher 8969da9560cSBryan Venteicher cn = xcn; 8979da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8986632efe4SBryan Venteicher 8999da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 90062a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 90162a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 90262a69c41SBryan Venteicher } 90310b59a9bSPeter Grehan } 90410b59a9bSPeter Grehan 90510b59a9bSPeter Grehan static int 9066632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 90710b59a9bSPeter Grehan { 90810b59a9bSPeter Grehan struct virtqueue *vq; 90910b59a9bSPeter Grehan int rc; 91010b59a9bSPeter Grehan 91110b59a9bSPeter Grehan vq = xvq; 9126632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 91310b59a9bSPeter Grehan 9146632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 91510b59a9bSPeter Grehan } 91610b59a9bSPeter Grehan 9176632efe4SBryan Venteicher static void 9186632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 9196632efe4SBryan Venteicher { 9206632efe4SBryan Venteicher struct virtqueue *vq; 9216632efe4SBryan Venteicher 9226632efe4SBryan Venteicher vq = xvq; 9236632efe4SBryan Venteicher virtqueue_intr(vq); 9246632efe4SBryan Venteicher } 9256632efe4SBryan Venteicher 9266632efe4SBryan Venteicher static void 9279da9560cSBryan Venteicher vtpci_config_intr(void *xcn) 92810b59a9bSPeter Grehan { 9299da9560cSBryan Venteicher struct vtpci_common *cn; 93010b59a9bSPeter Grehan device_t child; 93110b59a9bSPeter Grehan 9329da9560cSBryan Venteicher cn = xcn; 9339da9560cSBryan Venteicher child = cn->vtpci_child_dev; 93410b59a9bSPeter Grehan 93510b59a9bSPeter Grehan if (child != NULL) 9366632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 93710b59a9bSPeter Grehan } 938703f17d6SBryan Venteicher 939703f17d6SBryan Venteicher static int 940703f17d6SBryan Venteicher vtpci_feature_sysctl(struct sysctl_req *req, struct vtpci_common *cn, 941703f17d6SBryan Venteicher uint64_t features) 942703f17d6SBryan Venteicher { 943703f17d6SBryan Venteicher struct sbuf *sb; 944703f17d6SBryan Venteicher int error; 945703f17d6SBryan Venteicher 946703f17d6SBryan Venteicher sb = sbuf_new_for_sysctl(NULL, NULL, 256, req); 947703f17d6SBryan Venteicher if (sb == NULL) 948703f17d6SBryan Venteicher return (ENOMEM); 949703f17d6SBryan Venteicher 950703f17d6SBryan Venteicher error = virtio_describe_sbuf(sb, features, cn->vtpci_child_feat_desc); 951703f17d6SBryan Venteicher sbuf_delete(sb); 952703f17d6SBryan Venteicher 953703f17d6SBryan Venteicher return (error); 954703f17d6SBryan Venteicher } 955703f17d6SBryan Venteicher 956703f17d6SBryan Venteicher static int 957703f17d6SBryan Venteicher vtpci_host_features_sysctl(SYSCTL_HANDLER_ARGS) 958703f17d6SBryan Venteicher { 959703f17d6SBryan Venteicher struct vtpci_common *cn; 960703f17d6SBryan Venteicher 961703f17d6SBryan Venteicher cn = arg1; 962703f17d6SBryan Venteicher 963703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_host_features)); 964703f17d6SBryan Venteicher } 965703f17d6SBryan Venteicher 966703f17d6SBryan Venteicher static int 967703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl(SYSCTL_HANDLER_ARGS) 968703f17d6SBryan Venteicher { 969703f17d6SBryan Venteicher struct vtpci_common *cn; 970703f17d6SBryan Venteicher 971703f17d6SBryan Venteicher cn = arg1; 972703f17d6SBryan Venteicher 973703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_features)); 974703f17d6SBryan Venteicher } 975703f17d6SBryan Venteicher 976703f17d6SBryan Venteicher static void 977703f17d6SBryan Venteicher vtpci_setup_sysctl(struct vtpci_common *cn) 978703f17d6SBryan Venteicher { 979703f17d6SBryan Venteicher device_t dev; 980703f17d6SBryan Venteicher struct sysctl_ctx_list *ctx; 981703f17d6SBryan Venteicher struct sysctl_oid *tree; 982703f17d6SBryan Venteicher struct sysctl_oid_list *child; 983703f17d6SBryan Venteicher 984703f17d6SBryan Venteicher dev = cn->vtpci_dev; 985703f17d6SBryan Venteicher ctx = device_get_sysctl_ctx(dev); 986703f17d6SBryan Venteicher tree = device_get_sysctl_tree(dev); 987703f17d6SBryan Venteicher child = SYSCTL_CHILDREN(tree); 988703f17d6SBryan Venteicher 989703f17d6SBryan Venteicher SYSCTL_ADD_INT(ctx, child, OID_AUTO, "nvqs", 990703f17d6SBryan Venteicher CTLFLAG_RD, &cn->vtpci_nvqs, 0, "Number of virtqueues"); 991703f17d6SBryan Venteicher 992703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "host_features", 993703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 994703f17d6SBryan Venteicher vtpci_host_features_sysctl, "A", "Features supported by the host"); 995703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "negotiated_features", 996703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 997703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl, "A", "Features negotiated"); 998703f17d6SBryan Venteicher } 999