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 1715b56413dSWarner 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 { 185*11a91178SJohn Baldwin device_t dev; 18610b59a9bSPeter Grehan int error; 18710b59a9bSPeter Grehan 1889da9560cSBryan Venteicher dev = cn->vtpci_dev; 18910b59a9bSPeter Grehan 190*11a91178SJohn Baldwin error = bus_generic_detach(dev); 19110b59a9bSPeter Grehan if (error) 19210b59a9bSPeter Grehan return (error); 19310b59a9bSPeter Grehan 19410b59a9bSPeter Grehan return (0); 19510b59a9bSPeter Grehan } 19610b59a9bSPeter Grehan 1979da9560cSBryan Venteicher void 1989da9560cSBryan Venteicher vtpci_child_detached(struct vtpci_common *cn) 19910b59a9bSPeter Grehan { 20010b59a9bSPeter Grehan 2019da9560cSBryan Venteicher vtpci_release_child_resources(cn); 2029da9560cSBryan Venteicher 2039da9560cSBryan Venteicher cn->vtpci_child_feat_desc = NULL; 204703f17d6SBryan Venteicher cn->vtpci_host_features = 0; 2059da9560cSBryan Venteicher cn->vtpci_features = 0; 20610b59a9bSPeter Grehan } 20710b59a9bSPeter Grehan 2089da9560cSBryan Venteicher int 2099da9560cSBryan Venteicher vtpci_reinit(struct vtpci_common *cn) 21010b59a9bSPeter Grehan { 2119da9560cSBryan Venteicher int idx, error; 21210b59a9bSPeter Grehan 2139da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 2149da9560cSBryan Venteicher error = vtpci_reinit_virtqueue(cn, idx); 2159da9560cSBryan Venteicher if (error) 2169da9560cSBryan Venteicher return (error); 21710b59a9bSPeter Grehan } 21810b59a9bSPeter Grehan 2199da9560cSBryan Venteicher if (vtpci_is_msix_enabled(cn)) { 2209da9560cSBryan Venteicher error = vtpci_set_host_msix_vectors(cn); 2219da9560cSBryan Venteicher if (error) 2229da9560cSBryan Venteicher return (error); 2239da9560cSBryan Venteicher } 22410b59a9bSPeter Grehan 22510b59a9bSPeter Grehan return (0); 22610b59a9bSPeter Grehan } 22710b59a9bSPeter Grehan 22810b59a9bSPeter Grehan static void 2299da9560cSBryan Venteicher vtpci_describe_features(struct vtpci_common *cn, const char *msg, 2309da9560cSBryan Venteicher uint64_t features) 23110b59a9bSPeter Grehan { 2329da9560cSBryan Venteicher device_t dev, child; 23310b59a9bSPeter Grehan 2349da9560cSBryan Venteicher dev = cn->vtpci_dev; 2359da9560cSBryan Venteicher child = cn->vtpci_child_dev; 23610b59a9bSPeter Grehan 2379da9560cSBryan Venteicher if (device_is_attached(child) || bootverbose == 0) 2389da9560cSBryan Venteicher return; 2399da9560cSBryan Venteicher 2409da9560cSBryan Venteicher virtio_describe(dev, msg, features, cn->vtpci_child_feat_desc); 24110b59a9bSPeter Grehan } 24210b59a9bSPeter Grehan 2439da9560cSBryan Venteicher uint64_t 2449da9560cSBryan Venteicher vtpci_negotiate_features(struct vtpci_common *cn, 2459da9560cSBryan Venteicher uint64_t child_features, uint64_t host_features) 24610b59a9bSPeter Grehan { 2479da9560cSBryan Venteicher uint64_t features; 24810b59a9bSPeter Grehan 249703f17d6SBryan Venteicher cn->vtpci_host_features = host_features; 2509da9560cSBryan Venteicher vtpci_describe_features(cn, "host", host_features); 25110b59a9bSPeter Grehan 2529da9560cSBryan Venteicher /* 2539da9560cSBryan Venteicher * Limit negotiated features to what the driver, virtqueue, and 2549da9560cSBryan Venteicher * host all support. 2559da9560cSBryan Venteicher */ 2569da9560cSBryan Venteicher features = host_features & child_features; 2579da9560cSBryan Venteicher features = virtio_filter_transport_features(features); 2589da9560cSBryan Venteicher 2599da9560cSBryan Venteicher cn->vtpci_features = features; 260703f17d6SBryan Venteicher vtpci_describe_features(cn, "negotiated", features); 2619da9560cSBryan Venteicher 2629da9560cSBryan Venteicher return (features); 26310b59a9bSPeter Grehan } 26410b59a9bSPeter Grehan 265ccb576a8SMina Galić bool 2669da9560cSBryan Venteicher vtpci_with_feature(struct vtpci_common *cn, uint64_t feature) 26710b59a9bSPeter Grehan { 2689da9560cSBryan Venteicher return ((cn->vtpci_features & feature) != 0); 2699da9560cSBryan Venteicher } 27010b59a9bSPeter Grehan 2719da9560cSBryan Venteicher int 2729da9560cSBryan Venteicher vtpci_read_ivar(struct vtpci_common *cn, int index, uintptr_t *result) 2739da9560cSBryan Venteicher { 2749da9560cSBryan Venteicher device_t dev; 2759da9560cSBryan Venteicher int error; 27610b59a9bSPeter Grehan 2779da9560cSBryan Venteicher dev = cn->vtpci_dev; 2789da9560cSBryan Venteicher error = 0; 27910b59a9bSPeter Grehan 28010b59a9bSPeter Grehan switch (index) { 281310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 282310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 283310dacd0SPeter Grehan break; 284310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 285310dacd0SPeter Grehan *result = pci_get_vendor(dev); 286310dacd0SPeter Grehan break; 287310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 288310dacd0SPeter Grehan *result = pci_get_device(dev); 289310dacd0SPeter Grehan break; 290310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 2917cc8e55bSConrad Meyer *result = pci_get_subvendor(dev); 29210b59a9bSPeter Grehan break; 2939da9560cSBryan Venteicher case VIRTIO_IVAR_MODERN: 2949da9560cSBryan Venteicher *result = vtpci_is_modern(cn); 2959da9560cSBryan Venteicher break; 29610b59a9bSPeter Grehan default: 2979da9560cSBryan Venteicher error = ENOENT; 29810b59a9bSPeter Grehan } 29910b59a9bSPeter Grehan 3009da9560cSBryan Venteicher return (error); 30110b59a9bSPeter Grehan } 30210b59a9bSPeter Grehan 3039da9560cSBryan Venteicher int 3049da9560cSBryan Venteicher vtpci_write_ivar(struct vtpci_common *cn, int index, uintptr_t value) 30510b59a9bSPeter Grehan { 3069da9560cSBryan Venteicher int error; 30710b59a9bSPeter Grehan 3089da9560cSBryan Venteicher error = 0; 30910b59a9bSPeter Grehan 31010b59a9bSPeter Grehan switch (index) { 31110b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 3129da9560cSBryan Venteicher cn->vtpci_child_feat_desc = (void *) value; 31310b59a9bSPeter Grehan break; 31410b59a9bSPeter Grehan default: 3159da9560cSBryan Venteicher error = ENOENT; 31610b59a9bSPeter Grehan } 31710b59a9bSPeter Grehan 3189da9560cSBryan Venteicher return (error); 31910b59a9bSPeter Grehan } 32010b59a9bSPeter Grehan 3219da9560cSBryan Venteicher int 322180c0240SMina Galić vtpci_alloc_virtqueues(struct vtpci_common *cn, int nvqs, 32310b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 32410b59a9bSPeter Grehan { 3259da9560cSBryan Venteicher device_t dev; 3269da9560cSBryan Venteicher int idx, align, error; 32710b59a9bSPeter Grehan 3289da9560cSBryan Venteicher dev = cn->vtpci_dev; 32910b59a9bSPeter Grehan 3309da9560cSBryan Venteicher /* 3319da9560cSBryan Venteicher * This is VIRTIO_PCI_VRING_ALIGN from legacy VirtIO. In modern VirtIO, 3329da9560cSBryan Venteicher * the tables do not have to be allocated contiguously, but we do so 3339da9560cSBryan Venteicher * anyways. 3349da9560cSBryan Venteicher */ 3359da9560cSBryan Venteicher align = 4096; 3369da9560cSBryan Venteicher 3379da9560cSBryan Venteicher if (cn->vtpci_nvqs != 0) 338310dacd0SPeter Grehan return (EALREADY); 33962a69c41SBryan Venteicher if (nvqs <= 0) 34010b59a9bSPeter Grehan return (EINVAL); 34110b59a9bSPeter Grehan 3429da9560cSBryan Venteicher cn->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 34362a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 3449da9560cSBryan Venteicher if (cn->vtpci_vqs == NULL) 34562a69c41SBryan Venteicher return (ENOMEM); 34662a69c41SBryan Venteicher 347310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 3489da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 3499da9560cSBryan Venteicher struct vq_alloc_info *info; 3509da9560cSBryan Venteicher struct virtqueue *vq; 3519da9560cSBryan Venteicher bus_size_t notify_offset; 3529da9560cSBryan Venteicher uint16_t size; 3539da9560cSBryan Venteicher 3549da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 355310dacd0SPeter Grehan info = &vq_info[idx]; 356310dacd0SPeter Grehan 3579da9560cSBryan Venteicher size = vtpci_get_vq_size(cn, idx); 3589da9560cSBryan Venteicher notify_offset = vtpci_get_vq_notify_off(cn, idx); 359310dacd0SPeter Grehan 3609da9560cSBryan Venteicher error = virtqueue_alloc(dev, idx, size, notify_offset, align, 361776d3d59SKristof Provost ~(vm_paddr_t)0, info, &vq); 36210b59a9bSPeter Grehan if (error) { 363310dacd0SPeter Grehan device_printf(dev, 364310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 365310dacd0SPeter Grehan break; 36610b59a9bSPeter Grehan } 36710b59a9bSPeter Grehan 3689da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 36910b59a9bSPeter Grehan 37062a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 37162a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 372310dacd0SPeter Grehan 3739da9560cSBryan Venteicher cn->vtpci_nvqs++; 37410b59a9bSPeter Grehan } 37510b59a9bSPeter Grehan 37662a69c41SBryan Venteicher if (error) 3779da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 37862a69c41SBryan Venteicher 379310dacd0SPeter Grehan return (error); 38010b59a9bSPeter Grehan } 38110b59a9bSPeter Grehan 38210b59a9bSPeter Grehan static int 3839da9560cSBryan Venteicher vtpci_alloc_msix(struct vtpci_common *cn, int nvectors) 38410b59a9bSPeter Grehan { 385310dacd0SPeter Grehan device_t dev; 386310dacd0SPeter Grehan int nmsix, cnt, required; 38710b59a9bSPeter Grehan 3889da9560cSBryan Venteicher dev = cn->vtpci_dev; 38910b59a9bSPeter Grehan 390310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 391310dacd0SPeter Grehan required = nvectors + 1; 39210b59a9bSPeter Grehan 393310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 394310dacd0SPeter Grehan if (nmsix < required) 395310dacd0SPeter Grehan return (1); 396310dacd0SPeter Grehan 397310dacd0SPeter Grehan cnt = required; 398310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 3999da9560cSBryan Venteicher cn->vtpci_nmsix_resources = required; 400310dacd0SPeter Grehan return (0); 40110b59a9bSPeter Grehan } 40210b59a9bSPeter Grehan 403310dacd0SPeter Grehan pci_release_msi(dev); 40410b59a9bSPeter Grehan 405310dacd0SPeter Grehan return (1); 40610b59a9bSPeter Grehan } 40710b59a9bSPeter Grehan 40810b59a9bSPeter Grehan static int 4099da9560cSBryan Venteicher vtpci_alloc_msi(struct vtpci_common *cn) 410310dacd0SPeter Grehan { 411310dacd0SPeter Grehan device_t dev; 412310dacd0SPeter Grehan int nmsi, cnt, required; 413310dacd0SPeter Grehan 4149da9560cSBryan Venteicher dev = cn->vtpci_dev; 415310dacd0SPeter Grehan required = 1; 416310dacd0SPeter Grehan 417310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 418310dacd0SPeter Grehan if (nmsi < required) 419310dacd0SPeter Grehan return (1); 420310dacd0SPeter Grehan 421310dacd0SPeter Grehan cnt = required; 42262a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 423310dacd0SPeter Grehan return (0); 424310dacd0SPeter Grehan 425310dacd0SPeter Grehan pci_release_msi(dev); 426310dacd0SPeter Grehan 427310dacd0SPeter Grehan return (1); 428310dacd0SPeter Grehan } 429310dacd0SPeter Grehan 430310dacd0SPeter Grehan static int 4319da9560cSBryan Venteicher vtpci_alloc_intr_msix_pervq(struct vtpci_common *cn) 432310dacd0SPeter Grehan { 433310dacd0SPeter Grehan int i, nvectors, error; 434310dacd0SPeter Grehan 4359da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 436310dacd0SPeter Grehan return (ENOTSUP); 437310dacd0SPeter Grehan 4389da9560cSBryan Venteicher for (nvectors = 0, i = 0; i < cn->vtpci_nvqs; i++) { 4399da9560cSBryan Venteicher if (cn->vtpci_vqs[i].vtv_no_intr == 0) 440310dacd0SPeter Grehan nvectors++; 441310dacd0SPeter Grehan } 442310dacd0SPeter Grehan 4439da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, nvectors); 444310dacd0SPeter Grehan if (error) 445310dacd0SPeter Grehan return (error); 446310dacd0SPeter Grehan 4479da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX; 448310dacd0SPeter Grehan 449310dacd0SPeter Grehan return (0); 450310dacd0SPeter Grehan } 451310dacd0SPeter Grehan 452310dacd0SPeter Grehan static int 4539da9560cSBryan Venteicher vtpci_alloc_intr_msix_shared(struct vtpci_common *cn) 454310dacd0SPeter Grehan { 455310dacd0SPeter Grehan int error; 456310dacd0SPeter Grehan 4579da9560cSBryan Venteicher if (vtpci_disable_msix != 0 || cn->vtpci_flags & VTPCI_FLAG_NO_MSIX) 458310dacd0SPeter Grehan return (ENOTSUP); 459310dacd0SPeter Grehan 4609da9560cSBryan Venteicher error = vtpci_alloc_msix(cn, 1); 461310dacd0SPeter Grehan if (error) 462310dacd0SPeter Grehan return (error); 463310dacd0SPeter Grehan 4649da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 465310dacd0SPeter Grehan 466310dacd0SPeter Grehan return (0); 467310dacd0SPeter Grehan } 468310dacd0SPeter Grehan 469310dacd0SPeter Grehan static int 4709da9560cSBryan Venteicher vtpci_alloc_intr_msi(struct vtpci_common *cn) 471310dacd0SPeter Grehan { 472310dacd0SPeter Grehan int error; 473310dacd0SPeter Grehan 474310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 4759da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_NO_MSI) 476310dacd0SPeter Grehan return (ENOTSUP); 477310dacd0SPeter Grehan 4789da9560cSBryan Venteicher error = vtpci_alloc_msi(cn); 479310dacd0SPeter Grehan if (error) 480310dacd0SPeter Grehan return (error); 481310dacd0SPeter Grehan 4829da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_MSI; 483310dacd0SPeter Grehan 484310dacd0SPeter Grehan return (0); 485310dacd0SPeter Grehan } 486310dacd0SPeter Grehan 487310dacd0SPeter Grehan static int 4889da9560cSBryan Venteicher vtpci_alloc_intr_intx(struct vtpci_common *cn) 489310dacd0SPeter Grehan { 490310dacd0SPeter Grehan 4919da9560cSBryan Venteicher cn->vtpci_flags |= VTPCI_FLAG_INTX; 49262a69c41SBryan Venteicher 49362a69c41SBryan Venteicher return (0); 49462a69c41SBryan Venteicher } 49562a69c41SBryan Venteicher 49662a69c41SBryan Venteicher static int 4979da9560cSBryan Venteicher vtpci_alloc_interrupt(struct vtpci_common *cn, int rid, int flags, 49862a69c41SBryan Venteicher struct vtpci_interrupt *intr) 49962a69c41SBryan Venteicher { 50062a69c41SBryan Venteicher struct resource *irq; 50162a69c41SBryan Venteicher 5029da9560cSBryan Venteicher irq = bus_alloc_resource_any(cn->vtpci_dev, SYS_RES_IRQ, &rid, flags); 50362a69c41SBryan Venteicher if (irq == NULL) 50462a69c41SBryan Venteicher return (ENXIO); 50562a69c41SBryan Venteicher 50662a69c41SBryan Venteicher intr->vti_irq = irq; 50762a69c41SBryan Venteicher intr->vti_rid = rid; 508310dacd0SPeter Grehan 509310dacd0SPeter Grehan return (0); 510310dacd0SPeter Grehan } 511310dacd0SPeter Grehan 51210b59a9bSPeter Grehan static void 5139da9560cSBryan Venteicher vtpci_free_interrupt(struct vtpci_common *cn, struct vtpci_interrupt *intr) 51410b59a9bSPeter Grehan { 51510b59a9bSPeter Grehan device_t dev; 51610b59a9bSPeter Grehan 5179da9560cSBryan Venteicher dev = cn->vtpci_dev; 51810b59a9bSPeter Grehan 51962a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 52062a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 52162a69c41SBryan Venteicher intr->vti_handler = NULL; 52210b59a9bSPeter Grehan } 52310b59a9bSPeter Grehan 52462a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 52562a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 52662a69c41SBryan Venteicher intr->vti_irq); 52762a69c41SBryan Venteicher intr->vti_irq = NULL; 52862a69c41SBryan Venteicher intr->vti_rid = -1; 52962a69c41SBryan Venteicher } 53010b59a9bSPeter Grehan } 53110b59a9bSPeter Grehan 53262a69c41SBryan Venteicher static void 5339da9560cSBryan Venteicher vtpci_free_interrupts(struct vtpci_common *cn) 53462a69c41SBryan Venteicher { 53562a69c41SBryan Venteicher struct vtpci_interrupt *intr; 53662a69c41SBryan Venteicher int i, nvq_intrs; 53762a69c41SBryan Venteicher 5389da9560cSBryan Venteicher vtpci_free_interrupt(cn, &cn->vtpci_device_interrupt); 53962a69c41SBryan Venteicher 5409da9560cSBryan Venteicher if (cn->vtpci_nmsix_resources != 0) { 5419da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 5429da9560cSBryan Venteicher cn->vtpci_nmsix_resources = 0; 54362a69c41SBryan Venteicher 5449da9560cSBryan Venteicher if ((intr = cn->vtpci_msix_vq_interrupts) != NULL) { 54562a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 5469da9560cSBryan Venteicher vtpci_free_interrupt(cn, intr); 54762a69c41SBryan Venteicher 5489da9560cSBryan Venteicher free(cn->vtpci_msix_vq_interrupts, M_DEVBUF); 5499da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = NULL; 55062a69c41SBryan Venteicher } 55110b59a9bSPeter Grehan } 552310dacd0SPeter Grehan 5539da9560cSBryan Venteicher if (cn->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 5549da9560cSBryan Venteicher pci_release_msi(cn->vtpci_dev); 555310dacd0SPeter Grehan 5569da9560cSBryan Venteicher cn->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 55710b59a9bSPeter Grehan } 55810b59a9bSPeter Grehan 55910b59a9bSPeter Grehan static void 5609da9560cSBryan Venteicher vtpci_free_virtqueues(struct vtpci_common *cn) 56110b59a9bSPeter Grehan { 56210b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 56362a69c41SBryan Venteicher int idx; 56410b59a9bSPeter Grehan 5659da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 5669da9560cSBryan Venteicher vtpci_disable_vq(cn, idx); 56710b59a9bSPeter Grehan 5689da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 56962a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 57062a69c41SBryan Venteicher vqx->vtv_vq = NULL; 57110b59a9bSPeter Grehan } 572310dacd0SPeter Grehan 5739da9560cSBryan Venteicher free(cn->vtpci_vqs, M_DEVBUF); 5749da9560cSBryan Venteicher cn->vtpci_vqs = NULL; 5759da9560cSBryan Venteicher cn->vtpci_nvqs = 0; 57610b59a9bSPeter Grehan } 577310dacd0SPeter Grehan 5789da9560cSBryan Venteicher void 5799da9560cSBryan Venteicher vtpci_release_child_resources(struct vtpci_common *cn) 58062a69c41SBryan Venteicher { 58162a69c41SBryan Venteicher 5829da9560cSBryan Venteicher vtpci_free_interrupts(cn); 5839da9560cSBryan Venteicher vtpci_free_virtqueues(cn); 58462a69c41SBryan Venteicher } 58562a69c41SBryan Venteicher 58662a69c41SBryan Venteicher static void 5879da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(struct vtpci_common *cn) 588310dacd0SPeter Grehan { 589310dacd0SPeter Grehan int idx; 590310dacd0SPeter Grehan 5919da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_MSIX) { 5929da9560cSBryan Venteicher vtpci_register_cfg_msix(cn, NULL); 593310dacd0SPeter Grehan 5949da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) 5959da9560cSBryan Venteicher vtpci_register_vq_msix(cn, idx, NULL); 596310dacd0SPeter Grehan } 597310dacd0SPeter Grehan 5989da9560cSBryan Venteicher vtpci_free_interrupts(cn); 59910b59a9bSPeter Grehan } 60010b59a9bSPeter Grehan 6019da9560cSBryan Venteicher static int 6029da9560cSBryan Venteicher vtpci_alloc_intr_resources(struct vtpci_common *cn) 60310b59a9bSPeter Grehan { 6049da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6059da9560cSBryan Venteicher int i, rid, flags, nvq_intrs, error; 6069da9560cSBryan Venteicher 6079da9560cSBryan Venteicher flags = RF_ACTIVE; 6089da9560cSBryan Venteicher 6099da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) { 6109da9560cSBryan Venteicher rid = 0; 6119da9560cSBryan Venteicher flags |= RF_SHAREABLE; 6129da9560cSBryan Venteicher } else 6139da9560cSBryan Venteicher rid = 1; 61410b59a9bSPeter Grehan 61510b59a9bSPeter Grehan /* 6169da9560cSBryan Venteicher * When using INTX or MSI interrupts, this resource handles all 6179da9560cSBryan Venteicher * interrupts. When using MSIX, this resource handles just the 6189da9560cSBryan Venteicher * configuration changed interrupt. 61910b59a9bSPeter Grehan */ 6209da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6219da9560cSBryan Venteicher 6229da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6239da9560cSBryan Venteicher if (error || cn->vtpci_flags & (VTPCI_FLAG_INTX | VTPCI_FLAG_MSI)) 6249da9560cSBryan Venteicher return (error); 6259da9560cSBryan Venteicher 6269da9560cSBryan Venteicher /* 6279da9560cSBryan Venteicher * Now allocate the interrupts for the virtqueues. This may be one 6289da9560cSBryan Venteicher * for all the virtqueues, or one for each virtqueue. Subtract one 6299da9560cSBryan Venteicher * below for because of the configuration changed interrupt. 6309da9560cSBryan Venteicher */ 6319da9560cSBryan Venteicher nvq_intrs = cn->vtpci_nmsix_resources - 1; 6329da9560cSBryan Venteicher 6339da9560cSBryan Venteicher cn->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 6349da9560cSBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 6359da9560cSBryan Venteicher if (cn->vtpci_msix_vq_interrupts == NULL) 6369da9560cSBryan Venteicher return (ENOMEM); 6379da9560cSBryan Venteicher 6389da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6399da9560cSBryan Venteicher 6409da9560cSBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 6419da9560cSBryan Venteicher error = vtpci_alloc_interrupt(cn, rid, flags, intr); 6429da9560cSBryan Venteicher if (error) 6439da9560cSBryan Venteicher return (error); 6449da9560cSBryan Venteicher } 6459da9560cSBryan Venteicher 6469da9560cSBryan Venteicher return (0); 6479da9560cSBryan Venteicher } 6489da9560cSBryan Venteicher 6499da9560cSBryan Venteicher static int 6509da9560cSBryan Venteicher vtpci_setup_intx_interrupt(struct vtpci_common *cn, enum intr_type type) 6519da9560cSBryan Venteicher { 6529da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6539da9560cSBryan Venteicher int error; 6549da9560cSBryan Venteicher 6559da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6569da9560cSBryan Venteicher 6579da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 6589da9560cSBryan Venteicher vtpci_intx_intr, cn, &intr->vti_handler); 6599da9560cSBryan Venteicher 6609da9560cSBryan Venteicher return (error); 6619da9560cSBryan Venteicher } 6629da9560cSBryan Venteicher 6639da9560cSBryan Venteicher static int 6649da9560cSBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 6659da9560cSBryan Venteicher { 6669da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 6679da9560cSBryan Venteicher struct vtpci_interrupt *intr; 6689da9560cSBryan Venteicher int i, error; 6699da9560cSBryan Venteicher 6709da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 6719da9560cSBryan Venteicher 6729da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++) { 6739da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[i]; 6749da9560cSBryan Venteicher 6759da9560cSBryan Venteicher if (vqx->vtv_no_intr) 6769da9560cSBryan Venteicher continue; 6779da9560cSBryan Venteicher 6789da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 6799da9560cSBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 6809da9560cSBryan Venteicher &intr->vti_handler); 6819da9560cSBryan Venteicher if (error) 6829da9560cSBryan Venteicher return (error); 6839da9560cSBryan Venteicher 6849da9560cSBryan Venteicher intr++; 6859da9560cSBryan Venteicher } 6869da9560cSBryan Venteicher 6879da9560cSBryan Venteicher return (0); 6889da9560cSBryan Venteicher } 6899da9560cSBryan Venteicher 6909da9560cSBryan Venteicher static int 6919da9560cSBryan Venteicher vtpci_set_host_msix_vectors(struct vtpci_common *cn) 6929da9560cSBryan Venteicher { 6939da9560cSBryan Venteicher struct vtpci_interrupt *intr, *tintr; 6949da9560cSBryan Venteicher int idx, error; 6959da9560cSBryan Venteicher 6969da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 6979da9560cSBryan Venteicher error = vtpci_register_cfg_msix(cn, intr); 6989da9560cSBryan Venteicher if (error) 6999da9560cSBryan Venteicher return (error); 7009da9560cSBryan Venteicher 7019da9560cSBryan Venteicher intr = cn->vtpci_msix_vq_interrupts; 7029da9560cSBryan Venteicher for (idx = 0; idx < cn->vtpci_nvqs; idx++) { 7039da9560cSBryan Venteicher if (cn->vtpci_vqs[idx].vtv_no_intr) 7049da9560cSBryan Venteicher tintr = NULL; 7059da9560cSBryan Venteicher else 7069da9560cSBryan Venteicher tintr = intr; 7079da9560cSBryan Venteicher 7089da9560cSBryan Venteicher error = vtpci_register_vq_msix(cn, idx, tintr); 7099da9560cSBryan Venteicher if (error) 7109da9560cSBryan Venteicher break; 7119da9560cSBryan Venteicher 7129da9560cSBryan Venteicher /* 7139da9560cSBryan Venteicher * For shared MSIX, all the virtqueues share the first 7149da9560cSBryan Venteicher * interrupt. 7159da9560cSBryan Venteicher */ 7169da9560cSBryan Venteicher if (!cn->vtpci_vqs[idx].vtv_no_intr && 7179da9560cSBryan Venteicher (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 7189da9560cSBryan Venteicher intr++; 7199da9560cSBryan Venteicher } 7209da9560cSBryan Venteicher 7219da9560cSBryan Venteicher return (error); 7229da9560cSBryan Venteicher } 7239da9560cSBryan Venteicher 7249da9560cSBryan Venteicher static int 7259da9560cSBryan Venteicher vtpci_setup_msix_interrupts(struct vtpci_common *cn, enum intr_type type) 7269da9560cSBryan Venteicher { 7279da9560cSBryan Venteicher struct vtpci_interrupt *intr; 7289da9560cSBryan Venteicher int error; 7299da9560cSBryan Venteicher 7309da9560cSBryan Venteicher intr = &cn->vtpci_device_interrupt; 7319da9560cSBryan Venteicher 7329da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, NULL, 7339da9560cSBryan Venteicher vtpci_config_intr, cn, &intr->vti_handler); 7349da9560cSBryan Venteicher if (error) 7359da9560cSBryan Venteicher return (error); 7369da9560cSBryan Venteicher 7379da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 7389da9560cSBryan Venteicher intr = &cn->vtpci_msix_vq_interrupts[0]; 7399da9560cSBryan Venteicher 7409da9560cSBryan Venteicher error = bus_setup_intr(cn->vtpci_dev, intr->vti_irq, type, 7419da9560cSBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, cn, 7429da9560cSBryan Venteicher &intr->vti_handler); 7439da9560cSBryan Venteicher } else 7449da9560cSBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(cn, type); 7459da9560cSBryan Venteicher 7469da9560cSBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(cn)); 7479da9560cSBryan Venteicher } 7489da9560cSBryan Venteicher 7499da9560cSBryan Venteicher static int 7509da9560cSBryan Venteicher vtpci_setup_intrs(struct vtpci_common *cn, enum intr_type type) 7519da9560cSBryan Venteicher { 7529da9560cSBryan Venteicher int error; 7539da9560cSBryan Venteicher 7549da9560cSBryan Venteicher type |= INTR_MPSAFE; 7559da9560cSBryan Venteicher KASSERT(cn->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 7569da9560cSBryan Venteicher ("%s: no interrupt type selected %#x", __func__, cn->vtpci_flags)); 7579da9560cSBryan Venteicher 7589da9560cSBryan Venteicher error = vtpci_alloc_intr_resources(cn); 7599da9560cSBryan Venteicher if (error) 7609da9560cSBryan Venteicher return (error); 7619da9560cSBryan Venteicher 7629da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 7639da9560cSBryan Venteicher error = vtpci_setup_intx_interrupt(cn, type); 7649da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 7659da9560cSBryan Venteicher error = vtpci_setup_msi_interrupt(cn, type); 7669da9560cSBryan Venteicher else 7679da9560cSBryan Venteicher error = vtpci_setup_msix_interrupts(cn, type); 7689da9560cSBryan Venteicher 7699da9560cSBryan Venteicher return (error); 7709da9560cSBryan Venteicher } 7719da9560cSBryan Venteicher 7729da9560cSBryan Venteicher int 7739da9560cSBryan Venteicher vtpci_setup_interrupts(struct vtpci_common *cn, enum intr_type type) 7749da9560cSBryan Venteicher { 7759da9560cSBryan Venteicher device_t dev; 7769da9560cSBryan Venteicher int attempt, error; 7779da9560cSBryan Venteicher 7789da9560cSBryan Venteicher dev = cn->vtpci_dev; 7799da9560cSBryan Venteicher 7809da9560cSBryan Venteicher for (attempt = 0; attempt < 5; attempt++) { 7819da9560cSBryan Venteicher /* 7829da9560cSBryan Venteicher * Start with the most desirable interrupt configuration and 7839da9560cSBryan Venteicher * fallback towards less desirable ones. 7849da9560cSBryan Venteicher */ 7859da9560cSBryan Venteicher switch (attempt) { 7869da9560cSBryan Venteicher case 0: 7879da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_pervq(cn); 7889da9560cSBryan Venteicher break; 7899da9560cSBryan Venteicher case 1: 7909da9560cSBryan Venteicher error = vtpci_alloc_intr_msix_shared(cn); 7919da9560cSBryan Venteicher break; 7929da9560cSBryan Venteicher case 2: 7939da9560cSBryan Venteicher error = vtpci_alloc_intr_msi(cn); 7949da9560cSBryan Venteicher break; 7959da9560cSBryan Venteicher case 3: 7969da9560cSBryan Venteicher error = vtpci_alloc_intr_intx(cn); 7979da9560cSBryan Venteicher break; 7989da9560cSBryan Venteicher default: 7999da9560cSBryan Venteicher device_printf(dev, 8009da9560cSBryan Venteicher "exhausted all interrupt allocation attempts\n"); 8019da9560cSBryan Venteicher return (ENXIO); 8029da9560cSBryan Venteicher } 8039da9560cSBryan Venteicher 8049da9560cSBryan Venteicher if (error == 0 && vtpci_setup_intrs(cn, type) == 0) 8059da9560cSBryan Venteicher break; 8069da9560cSBryan Venteicher 8079da9560cSBryan Venteicher vtpci_cleanup_setup_intr_attempt(cn); 8089da9560cSBryan Venteicher } 8099da9560cSBryan Venteicher 8109da9560cSBryan Venteicher if (bootverbose) { 8119da9560cSBryan Venteicher if (cn->vtpci_flags & VTPCI_FLAG_INTX) 8129da9560cSBryan Venteicher device_printf(dev, "using legacy interrupt\n"); 8139da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_MSI) 8149da9560cSBryan Venteicher device_printf(dev, "using MSI interrupt\n"); 8159da9560cSBryan Venteicher else if (cn->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 8169da9560cSBryan Venteicher device_printf(dev, "using shared MSIX interrupts\n"); 8179da9560cSBryan Venteicher else 8189da9560cSBryan Venteicher device_printf(dev, "using per VQ MSIX interrupts\n"); 8199da9560cSBryan Venteicher } 8209da9560cSBryan Venteicher 8219da9560cSBryan Venteicher return (0); 8229da9560cSBryan Venteicher } 8239da9560cSBryan Venteicher 8249da9560cSBryan Venteicher static int 8259da9560cSBryan Venteicher vtpci_reinit_virtqueue(struct vtpci_common *cn, int idx) 8269da9560cSBryan Venteicher { 8279da9560cSBryan Venteicher struct vtpci_virtqueue *vqx; 8289da9560cSBryan Venteicher struct virtqueue *vq; 8299da9560cSBryan Venteicher int error; 8309da9560cSBryan Venteicher 8319da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[idx]; 8329da9560cSBryan Venteicher vq = vqx->vtv_vq; 8339da9560cSBryan Venteicher 8349da9560cSBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 8359da9560cSBryan Venteicher 8369da9560cSBryan Venteicher error = virtqueue_reinit(vq, vtpci_get_vq_size(cn, idx)); 8379da9560cSBryan Venteicher if (error == 0) 8389da9560cSBryan Venteicher vtpci_set_vq(cn, vq); 8399da9560cSBryan Venteicher 8409da9560cSBryan Venteicher return (error); 84110b59a9bSPeter Grehan } 84210b59a9bSPeter Grehan 843310dacd0SPeter Grehan static void 8449da9560cSBryan Venteicher vtpci_intx_intr(void *xcn) 845310dacd0SPeter Grehan { 8469da9560cSBryan Venteicher struct vtpci_common *cn; 84710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 84810b59a9bSPeter Grehan int i; 84910b59a9bSPeter Grehan uint8_t isr; 85010b59a9bSPeter Grehan 8519da9560cSBryan Venteicher cn = xcn; 8529da9560cSBryan Venteicher isr = vtpci_read_isr(cn); 85310b59a9bSPeter Grehan 85410b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 8559da9560cSBryan Venteicher vtpci_config_intr(cn); 85610b59a9bSPeter Grehan 8576632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 8589da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8599da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 86062a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 86162a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 86262a69c41SBryan Venteicher } 8636632efe4SBryan Venteicher } 86410b59a9bSPeter Grehan } 86510b59a9bSPeter Grehan 86610b59a9bSPeter Grehan static int 8679da9560cSBryan Venteicher vtpci_vq_shared_intr_filter(void *xcn) 86810b59a9bSPeter Grehan { 8699da9560cSBryan Venteicher struct vtpci_common *cn; 87010b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 87110b59a9bSPeter Grehan int i, rc; 87210b59a9bSPeter Grehan 8739da9560cSBryan Venteicher cn = xcn; 8749da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 87510b59a9bSPeter Grehan rc = 0; 87610b59a9bSPeter Grehan 8779da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 87862a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 87962a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 88062a69c41SBryan Venteicher } 88110b59a9bSPeter Grehan 8826632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 8836632efe4SBryan Venteicher } 8846632efe4SBryan Venteicher 8856632efe4SBryan Venteicher static void 8869da9560cSBryan Venteicher vtpci_vq_shared_intr(void *xcn) 8876632efe4SBryan Venteicher { 8889da9560cSBryan Venteicher struct vtpci_common *cn; 8896632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 8906632efe4SBryan Venteicher int i; 8916632efe4SBryan Venteicher 8929da9560cSBryan Venteicher cn = xcn; 8939da9560cSBryan Venteicher vqx = &cn->vtpci_vqs[0]; 8946632efe4SBryan Venteicher 8959da9560cSBryan Venteicher for (i = 0; i < cn->vtpci_nvqs; i++, vqx++) { 89662a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 89762a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 89862a69c41SBryan Venteicher } 89910b59a9bSPeter Grehan } 90010b59a9bSPeter Grehan 90110b59a9bSPeter Grehan static int 9026632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 90310b59a9bSPeter Grehan { 90410b59a9bSPeter Grehan struct virtqueue *vq; 90510b59a9bSPeter Grehan int rc; 90610b59a9bSPeter Grehan 90710b59a9bSPeter Grehan vq = xvq; 9086632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 90910b59a9bSPeter Grehan 9106632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 91110b59a9bSPeter Grehan } 91210b59a9bSPeter Grehan 9136632efe4SBryan Venteicher static void 9146632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 9156632efe4SBryan Venteicher { 9166632efe4SBryan Venteicher struct virtqueue *vq; 9176632efe4SBryan Venteicher 9186632efe4SBryan Venteicher vq = xvq; 9196632efe4SBryan Venteicher virtqueue_intr(vq); 9206632efe4SBryan Venteicher } 9216632efe4SBryan Venteicher 9226632efe4SBryan Venteicher static void 9239da9560cSBryan Venteicher vtpci_config_intr(void *xcn) 92410b59a9bSPeter Grehan { 9259da9560cSBryan Venteicher struct vtpci_common *cn; 92610b59a9bSPeter Grehan device_t child; 92710b59a9bSPeter Grehan 9289da9560cSBryan Venteicher cn = xcn; 9299da9560cSBryan Venteicher child = cn->vtpci_child_dev; 93010b59a9bSPeter Grehan 93110b59a9bSPeter Grehan if (child != NULL) 9326632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 93310b59a9bSPeter Grehan } 934703f17d6SBryan Venteicher 935703f17d6SBryan Venteicher static int 936703f17d6SBryan Venteicher vtpci_feature_sysctl(struct sysctl_req *req, struct vtpci_common *cn, 937703f17d6SBryan Venteicher uint64_t features) 938703f17d6SBryan Venteicher { 939703f17d6SBryan Venteicher struct sbuf *sb; 940703f17d6SBryan Venteicher int error; 941703f17d6SBryan Venteicher 942703f17d6SBryan Venteicher sb = sbuf_new_for_sysctl(NULL, NULL, 256, req); 943703f17d6SBryan Venteicher if (sb == NULL) 944703f17d6SBryan Venteicher return (ENOMEM); 945703f17d6SBryan Venteicher 946703f17d6SBryan Venteicher error = virtio_describe_sbuf(sb, features, cn->vtpci_child_feat_desc); 947703f17d6SBryan Venteicher sbuf_delete(sb); 948703f17d6SBryan Venteicher 949703f17d6SBryan Venteicher return (error); 950703f17d6SBryan Venteicher } 951703f17d6SBryan Venteicher 952703f17d6SBryan Venteicher static int 953703f17d6SBryan Venteicher vtpci_host_features_sysctl(SYSCTL_HANDLER_ARGS) 954703f17d6SBryan Venteicher { 955703f17d6SBryan Venteicher struct vtpci_common *cn; 956703f17d6SBryan Venteicher 957703f17d6SBryan Venteicher cn = arg1; 958703f17d6SBryan Venteicher 959703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_host_features)); 960703f17d6SBryan Venteicher } 961703f17d6SBryan Venteicher 962703f17d6SBryan Venteicher static int 963703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl(SYSCTL_HANDLER_ARGS) 964703f17d6SBryan Venteicher { 965703f17d6SBryan Venteicher struct vtpci_common *cn; 966703f17d6SBryan Venteicher 967703f17d6SBryan Venteicher cn = arg1; 968703f17d6SBryan Venteicher 969703f17d6SBryan Venteicher return (vtpci_feature_sysctl(req, cn, cn->vtpci_features)); 970703f17d6SBryan Venteicher } 971703f17d6SBryan Venteicher 972703f17d6SBryan Venteicher static void 973703f17d6SBryan Venteicher vtpci_setup_sysctl(struct vtpci_common *cn) 974703f17d6SBryan Venteicher { 975703f17d6SBryan Venteicher device_t dev; 976703f17d6SBryan Venteicher struct sysctl_ctx_list *ctx; 977703f17d6SBryan Venteicher struct sysctl_oid *tree; 978703f17d6SBryan Venteicher struct sysctl_oid_list *child; 979703f17d6SBryan Venteicher 980703f17d6SBryan Venteicher dev = cn->vtpci_dev; 981703f17d6SBryan Venteicher ctx = device_get_sysctl_ctx(dev); 982703f17d6SBryan Venteicher tree = device_get_sysctl_tree(dev); 983703f17d6SBryan Venteicher child = SYSCTL_CHILDREN(tree); 984703f17d6SBryan Venteicher 985703f17d6SBryan Venteicher SYSCTL_ADD_INT(ctx, child, OID_AUTO, "nvqs", 986703f17d6SBryan Venteicher CTLFLAG_RD, &cn->vtpci_nvqs, 0, "Number of virtqueues"); 987703f17d6SBryan Venteicher 988703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "host_features", 989703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 990703f17d6SBryan Venteicher vtpci_host_features_sysctl, "A", "Features supported by the host"); 991703f17d6SBryan Venteicher SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "negotiated_features", 992703f17d6SBryan Venteicher CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, cn, 0, 993703f17d6SBryan Venteicher vtpci_negotiated_features_sysctl, "A", "Features negotiated"); 994703f17d6SBryan Venteicher } 995