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