Date: Sun, 25 Mar 2018 00:57:00 +0000 (UTC) From: Mark Peek <mp@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r331510 - in head: share/man/man4 sys/conf sys/dev/vmware/vmci sys/modules/vmware sys/modules/vmware/vmci Message-ID: <201803250057.w2P0v0lA081039@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mp Date: Sun Mar 25 00:57:00 2018 New Revision: 331510 URL: https://svnweb.freebsd.org/changeset/base/331510 Log: Add VMCI (Virtual Machine Communication Interface) driver In a virtual machine, VMCI is exposed as a regular PCI device. The primary communication mechanisms supported are a point-to-point bidirectional transport based on a pair of memory-mapped queues, and asynchronous notifications in the form of datagrams and doorbells. These features are available to kernel level components such as vSockets through the VMCI kernel API. In addition to this, the VMCI kernel API provides support for receiving events related to the state of the VMCI communication channels, and the virtual machine itself. Submitted by: Vishnu Dasa <vdasa@vmware.com> Reviewed by: bcr, imp Obtained from: VMware Differential Revision: https://reviews.freebsd.org/D14289 Added: head/share/man/man4/vmci.4 (contents, props changed) head/sys/dev/vmware/vmci/ head/sys/dev/vmware/vmci/vmci.c (contents, props changed) head/sys/dev/vmware/vmci/vmci.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_call_defs.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_datagram.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_datagram.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_defs.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_doorbell.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_doorbell.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_driver.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_driver.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_event.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_event.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_hashtable.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_hashtable.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_api.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_api_1.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_api_2.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_defs.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_if.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_kernel_if.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_qpair.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_queue.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_queue_pair.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_queue_pair.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_resource.c (contents, props changed) head/sys/dev/vmware/vmci/vmci_resource.h (contents, props changed) head/sys/dev/vmware/vmci/vmci_utils.h (contents, props changed) head/sys/modules/vmware/vmci/ head/sys/modules/vmware/vmci/Makefile (contents, props changed) Modified: head/sys/conf/files.amd64 head/sys/conf/files.i386 head/sys/modules/vmware/Makefile Added: head/share/man/man4/vmci.4 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/share/man/man4/vmci.4 Sun Mar 25 00:57:00 2018 (r331510) @@ -0,0 +1,71 @@ +.\" Copyright (c) 2018 VMware, Inc. All Rights Reserved. +.\" +.\" SPDX-License-Identifier: (BSD-2-Clause AND GPL-2.0) +.\" +.\" $FreeBSD$ +.Dd February 10, 2018 +.Dt VMCI 4 +.Os +.Sh NAME +.Nm vmci +.Nd VMware Virtual Machine Communication Interface +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device vmci" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_vmci_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for the VMware Virtual Machine Communication Interface +(VMCI) in virtual machines by VMware. +.Pp +VMCI allows virtual machines to communicate with host kernel modules and the +VMware hypervisors. +User level applications in a virtual machine can use VMCI through vSockets +(also known as VMCI Sockets and not included in this kernel module), a socket +address family designed to be compatible with UDP and TCP at the interface +level. +Today, VMCI and vSockets are used by various VMware Tools components inside +the guest for zero-config, network-less access to VMware host services. +In addition to this, VMware's users are using vSockets for various +applications, where network access of the virtual machine is restricted +or non-existent. +Examples of this are VMs communicating with device proxies for proprietary +hardware running as host applications and automated testing of applications +running within virtual machines. +.Pp +In a virtual machine, VMCI is exposed as a regular PCI device. +The primary communication mechanisms supported are a point-to-point +bidirectional transport based on a pair of memory-mapped queues, and +asynchronous notifications in the form of datagrams and doorbells. +These features are available to kernel level components such as vSockets +through the VMCI kernel API. +In addition to this, the VMCI kernel API provides support for receiving +events related to the state of the VMCI communication channels, and the +virtual machine itself. +.Sh SEE ALSO +.Xr pci 9 , +.Xr socket 2 +.Rs +.%T "VMware vSockets Documentation" +.%U https://www.vmware.com/support/developer/vmci-sdk/ +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 12.0 . +.Sh AUTHORS +The +.Nm +driver and man page were written by +.An Vishnu Dasa Aq Mt vdasahar@gmail.com . Modified: head/sys/conf/files.amd64 ============================================================================== --- head/sys/conf/files.amd64 Sun Mar 25 00:50:27 2018 (r331509) +++ head/sys/conf/files.amd64 Sun Mar 25 00:57:00 2018 (r331510) @@ -475,6 +475,16 @@ dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_x86.c optional uart dev/viawd/viawd.c optional viawd dev/vmware/vmxnet3/if_vmx.c optional vmx +dev/vmware/vmci/vmci.c optional vmci +dev/vmware/vmci/vmci_datagram.c optional vmci +dev/vmware/vmci/vmci_doorbell.c optional vmci +dev/vmware/vmci/vmci_driver.c optional vmci +dev/vmware/vmci/vmci_event.c optional vmci +dev/vmware/vmci/vmci_hashtable.c optional vmci +dev/vmware/vmci/vmci_kernel_if.c optional vmci +dev/vmware/vmci/vmci_qpair.c optional vmci +dev/vmware/vmci/vmci_queue_pair.c optional vmci +dev/vmware/vmci/vmci_resource.c optional vmci dev/wbwd/wbwd.c optional wbwd dev/xen/pci/xen_acpi_pci.c optional xenhvm dev/xen/pci/xen_pci.c optional xenhvm Modified: head/sys/conf/files.i386 ============================================================================== --- head/sys/conf/files.i386 Sun Mar 25 00:50:27 2018 (r331509) +++ head/sys/conf/files.i386 Sun Mar 25 00:57:00 2018 (r331510) @@ -327,6 +327,16 @@ dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_x86.c optional uart dev/viawd/viawd.c optional viawd dev/vmware/vmxnet3/if_vmx.c optional vmx +dev/vmware/vmci/vmci.c optional vmci +dev/vmware/vmci/vmci_datagram.c optional vmci +dev/vmware/vmci/vmci_doorbell.c optional vmci +dev/vmware/vmci/vmci_driver.c optional vmci +dev/vmware/vmci/vmci_event.c optional vmci +dev/vmware/vmci/vmci_hashtable.c optional vmci +dev/vmware/vmci/vmci_kernel_if.c optional vmci +dev/vmware/vmci/vmci_qpair.c optional vmci +dev/vmware/vmci/vmci_queue_pair.c optional vmci +dev/vmware/vmci/vmci_resource.c optional vmci dev/acpica/acpi_if.m standard dev/acpica/acpi_hpet.c optional acpi dev/acpica/acpi_timer.c optional acpi Added: head/sys/dev/vmware/vmci/vmci.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/vmware/vmci/vmci.c Sun Mar 25 00:57:00 2018 (r331510) @@ -0,0 +1,1177 @@ +/*- + * Copyright (c) 2018 VMware, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: (BSD-2-Clause AND GPL-2.0) + */ + +/* Driver for VMware Virtual Machine Communication Interface (VMCI) device. */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/systm.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <machine/bus.h> + +#include "vmci.h" +#include "vmci_doorbell.h" +#include "vmci_driver.h" +#include "vmci_kernel_defs.h" +#include "vmci_queue_pair.h" + +static int vmci_probe(device_t); +static int vmci_attach(device_t); +static int vmci_detach(device_t); +static int vmci_shutdown(device_t); + +static int vmci_map_bars(struct vmci_softc *); +static void vmci_unmap_bars(struct vmci_softc *); + +static int vmci_config_capabilities(struct vmci_softc *); + +static int vmci_dma_malloc_int(struct vmci_softc *, bus_size_t, + bus_size_t, struct vmci_dma_alloc *); +static void vmci_dma_free_int(struct vmci_softc *, + struct vmci_dma_alloc *); + +static int vmci_config_interrupts(struct vmci_softc *); +static int vmci_config_interrupt(struct vmci_softc *); +static int vmci_check_intr_cnt(struct vmci_softc *); +static int vmci_allocate_interrupt_resources(struct vmci_softc *); +static int vmci_setup_interrupts(struct vmci_softc *); +static void vmci_dismantle_interrupts(struct vmci_softc *); +static void vmci_interrupt(void *); +static void vmci_interrupt_bm(void *); +static void dispatch_datagrams(void *, int); +static void process_bitmap(void *, int); + +static void vmci_delayed_work_fn_cb(void *context, int data); + +static device_method_t vmci_methods[] = { + /* Device interface. */ + DEVMETHOD(device_probe, vmci_probe), + DEVMETHOD(device_attach, vmci_attach), + DEVMETHOD(device_detach, vmci_detach), + DEVMETHOD(device_shutdown, vmci_shutdown), + + DEVMETHOD_END +}; + +static driver_t vmci_driver = { + "vmci", vmci_methods, sizeof(struct vmci_softc) +}; + +static devclass_t vmci_devclass; +DRIVER_MODULE(vmci, pci, vmci_driver, vmci_devclass, 0, 0); +MODULE_VERSION(vmci, VMCI_VERSION); + +MODULE_DEPEND(vmci, pci, 1, 1, 1); + +static struct vmci_softc *vmci_sc; + +#define LGPFX "vmci: " +/* + * Allocate a buffer for incoming datagrams globally to avoid repeated + * allocation in the interrupt handler's atomic context. + */ +static uint8_t *data_buffer = NULL; +static uint32_t data_buffer_size = VMCI_MAX_DG_SIZE; + +struct vmci_delayed_work_info { + vmci_work_fn *work_fn; + void *data; + vmci_list_item(vmci_delayed_work_info) entry; +}; + +/* + *------------------------------------------------------------------------------ + * + * vmci_probe -- + * + * Probe to see if the VMCI device is present. + * + * Results: + * BUS_PROBE_DEFAULT if device exists, ENXIO otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_probe(device_t dev) +{ + + if (pci_get_vendor(dev) == VMCI_VMWARE_VENDOR_ID && + pci_get_device(dev) == VMCI_VMWARE_DEVICE_ID) { + device_set_desc(dev, + "VMware Virtual Machine Communication Interface"); + + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_attach -- + * + * Attach VMCI device to the system after vmci_probe() has been called and + * the device has been detected. + * + * Results: + * 0 if success, ENXIO otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_attach(device_t dev) +{ + struct vmci_softc *sc; + int error, i; + + sc = device_get_softc(dev); + sc->vmci_dev = dev; + vmci_sc = sc; + + data_buffer = NULL; + sc->vmci_num_intr = 0; + for (i = 0; i < VMCI_MAX_INTRS; i++) { + sc->vmci_intrs[i].vmci_irq = NULL; + sc->vmci_intrs[i].vmci_handler = NULL; + } + + TASK_INIT(&sc->vmci_interrupt_dq_task, 0, dispatch_datagrams, sc); + TASK_INIT(&sc->vmci_interrupt_bm_task, 0, process_bitmap, sc); + + TASK_INIT(&sc->vmci_delayed_work_task, 0, vmci_delayed_work_fn_cb, sc); + + pci_enable_busmaster(dev); + + mtx_init(&sc->vmci_spinlock, "VMCI Spinlock", NULL, MTX_SPIN); + mtx_init(&sc->vmci_delayed_work_lock, "VMCI Delayed Work Lock", + NULL, MTX_DEF); + + error = vmci_map_bars(sc); + if (error) { + VMCI_LOG_ERROR(LGPFX"Failed to map PCI BARs.\n"); + goto fail; + } + + error = vmci_config_capabilities(sc); + if (error) { + VMCI_LOG_ERROR(LGPFX"Failed to configure capabilities.\n"); + goto fail; + } + + vmci_list_init(&sc->vmci_delayed_work_infos); + + vmci_components_init(); + vmci_util_init(); + error = vmci_qp_guest_endpoints_init(); + if (error) { + VMCI_LOG_ERROR(LGPFX"vmci_qp_guest_endpoints_init failed.\n"); + goto fail; + } + + error = vmci_config_interrupts(sc); + if (error) + VMCI_LOG_ERROR(LGPFX"Failed to enable interrupts.\n"); + +fail: + if (error) { + vmci_detach(dev); + return (ENXIO); + } + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_detach -- + * + * Detach the VMCI device. + * + * Results: + * 0 + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_detach(device_t dev) +{ + struct vmci_softc *sc; + + sc = device_get_softc(dev); + + vmci_qp_guest_endpoints_exit(); + vmci_util_exit(); + + vmci_dismantle_interrupts(sc); + + vmci_components_cleanup(); + + taskqueue_drain(taskqueue_thread, &sc->vmci_delayed_work_task); + mtx_destroy(&sc->vmci_delayed_work_lock); + + if (sc->vmci_res0 != NULL) + bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_CONTROL_ADDR, VMCI_CONTROL_RESET); + + if (sc->vmci_notifications_bitmap.dma_vaddr != NULL) + vmci_dma_free(&sc->vmci_notifications_bitmap); + + vmci_unmap_bars(sc); + + mtx_destroy(&sc->vmci_spinlock); + + pci_disable_busmaster(dev); + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_shutdown -- + * + * This function is called during system shutdown. We don't do anything. + * + * Results: + * 0 + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_shutdown(device_t dev) +{ + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_map_bars -- + * + * Maps the PCI I/O and MMIO BARs. + * + * Results: + * 0 on success, ENXIO otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_map_bars(struct vmci_softc *sc) +{ + int rid; + + /* Map the PCI I/O BAR: BAR0 */ + rid = PCIR_BAR(0); + sc->vmci_res0 = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_IOPORT, + &rid, RF_ACTIVE); + if (sc->vmci_res0 == NULL) { + VMCI_LOG_ERROR(LGPFX"Could not map: BAR0\n"); + return (ENXIO); + } + + sc->vmci_iot0 = rman_get_bustag(sc->vmci_res0); + sc->vmci_ioh0 = rman_get_bushandle(sc->vmci_res0); + sc->vmci_ioaddr = rman_get_start(sc->vmci_res0); + + /* Map the PCI MMIO BAR: BAR1 */ + rid = PCIR_BAR(1); + sc->vmci_res1 = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->vmci_res1 == NULL) { + VMCI_LOG_ERROR(LGPFX"Could not map: BAR1\n"); + return (ENXIO); + } + + sc->vmci_iot1 = rman_get_bustag(sc->vmci_res1); + sc->vmci_ioh1 = rman_get_bushandle(sc->vmci_res1); + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_unmap_bars -- + * + * Unmaps the VMCI PCI I/O and MMIO BARs. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static void +vmci_unmap_bars(struct vmci_softc *sc) +{ + int rid; + + if (sc->vmci_res0 != NULL) { + rid = PCIR_BAR(0); + bus_release_resource(sc->vmci_dev, SYS_RES_IOPORT, rid, + sc->vmci_res0); + sc->vmci_res0 = NULL; + } + + if (sc->vmci_res1 != NULL) { + rid = PCIR_BAR(1); + bus_release_resource(sc->vmci_dev, SYS_RES_MEMORY, rid, + sc->vmci_res1); + sc->vmci_res1 = NULL; + } +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_config_capabilities -- + * + * Check the VMCI device capabilities and configure the device accordingly. + * + * Results: + * 0 if success, ENODEV otherwise. + * + * Side effects: + * Device capabilities are enabled. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_config_capabilities(struct vmci_softc *sc) +{ + unsigned long bitmap_PPN; + int error; + + /* + * Verify that the VMCI device supports the capabilities that we + * need. Datagrams are necessary and notifications will be used + * if the device supports it. + */ + sc->capabilities = bus_space_read_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_CAPS_ADDR); + + if ((sc->capabilities & VMCI_CAPS_DATAGRAM) == 0) { + VMCI_LOG_ERROR(LGPFX"VMCI device does not support " + "datagrams.\n"); + return (ENODEV); + } + + if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS) { + sc->capabilities = VMCI_CAPS_DATAGRAM; + error = vmci_dma_malloc(PAGE_SIZE, 1, + &sc->vmci_notifications_bitmap); + if (error) + VMCI_LOG_ERROR(LGPFX"Failed to alloc memory for " + "notification bitmap.\n"); + else { + memset(sc->vmci_notifications_bitmap.dma_vaddr, 0, + PAGE_SIZE); + sc->capabilities |= VMCI_CAPS_NOTIFICATIONS; + } + } else + sc->capabilities = VMCI_CAPS_DATAGRAM; + + /* Let the host know which capabilities we intend to use. */ + bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_CAPS_ADDR, sc->capabilities); + + /* + * Register notification bitmap with device if that capability is + * used. + */ + if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS) { + bitmap_PPN = + sc->vmci_notifications_bitmap.dma_paddr >> PAGE_SHIFT; + vmci_register_notification_bitmap(bitmap_PPN); + } + + /* Check host capabilities. */ + if (!vmci_check_host_capabilities()) + return (ENODEV); + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_dmamap_cb -- + * + * Callback to receive mapping information resulting from the load of a + * bus_dmamap_t via bus_dmamap_load() + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static void +vmci_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *baddr = arg; + + if (error == 0) + *baddr = segs->ds_addr; +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_dma_malloc_int -- + * + * Internal function that allocates DMA memory. + * + * Results: + * 0 if success. + * ENOMEM if insufficient memory. + * EINPROGRESS if mapping is deferred. + * EINVAL if the request was invalid. + * + * Side effects: + * DMA memory is allocated. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_dma_malloc_int(struct vmci_softc *sc, bus_size_t size, bus_size_t align, + struct vmci_dma_alloc *dma) +{ + int error; + + bzero(dma, sizeof(struct vmci_dma_alloc)); + + error = bus_dma_tag_create(bus_get_dma_tag(vmci_sc->vmci_dev), + align, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &dma->dma_tag); + if (error) { + VMCI_LOG_ERROR(LGPFX"bus_dma_tag_create failed: %d\n", error); + goto fail; + } + + error = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_ZERO | BUS_DMA_NOWAIT, &dma->dma_map); + if (error) { + VMCI_LOG_ERROR(LGPFX"bus_dmamem_alloc failed: %d\n", error); + goto fail; + } + + error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, + size, vmci_dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT); + if (error) { + VMCI_LOG_ERROR(LGPFX"bus_dmamap_load failed: %d\n", error); + goto fail; + } + + dma->dma_size = size; + +fail: + if (error) + vmci_dma_free(dma); + + return (error); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_dma_malloc -- + * + * This function is a wrapper around vmci_dma_malloc_int for callers + * outside of this module. Since we only support a single VMCI device, this + * wrapper provides access to the device softc structure. + * + * Results: + * 0 if success. + * ENOMEM if insufficient memory. + * EINPROGRESS if mapping is deferred. + * EINVAL if the request was invalid. + * + * Side effects: + * DMA memory is allocated. + * + *------------------------------------------------------------------------------ + */ + +int +vmci_dma_malloc(bus_size_t size, bus_size_t align, struct vmci_dma_alloc *dma) +{ + + return (vmci_dma_malloc_int(vmci_sc, size, align, dma)); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_dma_free_int -- + * + * Internal function that frees DMA memory. + * + * Results: + * None. + * + * Side effects: + * Frees DMA memory. + * + *------------------------------------------------------------------------------ + */ + +static void +vmci_dma_free_int(struct vmci_softc *sc, struct vmci_dma_alloc *dma) +{ + + if (dma->dma_tag != NULL) { + if (dma->dma_paddr != 0) { + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + } + + if (dma->dma_vaddr != NULL) + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, + dma->dma_map); + + bus_dma_tag_destroy(dma->dma_tag); + } + bzero(dma, sizeof(struct vmci_dma_alloc)); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_dma_free -- + * + * This function is a wrapper around vmci_dma_free_int for callers outside + * of this module. Since we only support a single VMCI device, this wrapper + * provides access to the device softc structure. + * + * Results: + * None. + * + * Side effects: + * Frees DMA memory. + * + *------------------------------------------------------------------------------ + */ + +void +vmci_dma_free(struct vmci_dma_alloc *dma) +{ + + vmci_dma_free_int(vmci_sc, dma); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_config_interrupts -- + * + * Configures and enables interrupts. Try to configure MSI-X. If this fails, + * try to configure MSI. If even this fails, try legacy interrupts. + * + * Results: + * 0 if success. + * ENOMEM if insufficient memory. + * ENODEV if the device doesn't support interrupts. + * ENXIO if the device configuration failed. + * + * Side effects: + * Interrupts get enabled if successful. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_config_interrupts(struct vmci_softc *sc) +{ + int error; + + data_buffer = malloc(data_buffer_size, M_DEVBUF, M_ZERO | M_NOWAIT); + if (data_buffer == NULL) + return (ENOMEM); + + sc->vmci_intr_type = VMCI_INTR_TYPE_MSIX; + error = vmci_config_interrupt(sc); + if (error) { + sc->vmci_intr_type = VMCI_INTR_TYPE_MSI; + error = vmci_config_interrupt(sc); + } + if (error) { + sc->vmci_intr_type = VMCI_INTR_TYPE_INTX; + error = vmci_config_interrupt(sc); + } + if (error) + return (error); + + /* Enable specific interrupt bits. */ + if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS) + bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_IMR_ADDR, VMCI_IMR_DATAGRAM | VMCI_IMR_NOTIFICATION); + else + bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_IMR_ADDR, VMCI_IMR_DATAGRAM); + + /* Enable interrupts. */ + bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0, + VMCI_CONTROL_ADDR, VMCI_CONTROL_INT_ENABLE); + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_config_interrupt -- + * + * Check the number of interrupts supported, allocate resources and setup + * interrupts. + * + * Results: + * 0 if success. + * ENOMEM if insufficient memory. + * ENODEV if the device doesn't support interrupts. + * ENXIO if the device configuration failed. + * + * Side effects: + * Resources get allocated and interrupts get setup (but not enabled) if + * successful. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_config_interrupt(struct vmci_softc *sc) +{ + int error; + + error = vmci_check_intr_cnt(sc); + if (error) + return (error); + + error = vmci_allocate_interrupt_resources(sc); + if (error) + return (error); + + error = vmci_setup_interrupts(sc); + if (error) + return (error); + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_check_intr_cnt -- + * + * Check the number of interrupts supported by the device and ask PCI bus + * to allocate appropriate number of interrupts. + * + * Results: + * 0 if success. + * ENODEV if the device doesn't support any interrupts. + * ENXIO if the device configuration failed. + * + * Side effects: + * Resources get allocated on success. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_check_intr_cnt(struct vmci_softc *sc) +{ + + if (sc->vmci_intr_type == VMCI_INTR_TYPE_INTX) { + sc->vmci_num_intr = 1; + return (0); + } + + /* + * Make sure that the device supports the required number of MSI/MSI-X + * messages. We try for 2 MSI-X messages but 1 is good too. We need at + * least 1 MSI message. + */ + sc->vmci_num_intr = (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) ? + pci_msix_count(sc->vmci_dev) : pci_msi_count(sc->vmci_dev); + + if (!sc->vmci_num_intr) { + VMCI_LOG_ERROR(LGPFX"Device does not support any interrupt" + " messages"); + return (ENODEV); + } + + sc->vmci_num_intr = (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) ? + VMCI_MAX_INTRS : 1; + if (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) { + if (pci_alloc_msix(sc->vmci_dev, &sc->vmci_num_intr)) + return (ENXIO); + } else if (sc->vmci_intr_type == VMCI_INTR_TYPE_MSI) { + if (pci_alloc_msi(sc->vmci_dev, &sc->vmci_num_intr)) + return (ENXIO); + } + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_allocate_interrupt_resources -- + * + * Allocate resources necessary for interrupts. + * + * Results: + * 0 if success, ENXIO otherwise. + * + * Side effects: + * Resources get allocated on success. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_allocate_interrupt_resources(struct vmci_softc *sc) +{ + struct resource *irq; + int flags, i, rid; + + flags = RF_ACTIVE; + flags |= (sc->vmci_num_intr == 1) ? RF_SHAREABLE : 0; + rid = (sc->vmci_intr_type == VMCI_INTR_TYPE_INTX) ? 0 : 1; + + for (i = 0; i < sc->vmci_num_intr; i++, rid++) { + irq = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_IRQ, &rid, + flags); + if (irq == NULL) + return (ENXIO); + sc->vmci_intrs[i].vmci_irq = irq; + sc->vmci_intrs[i].vmci_rid = rid; + } + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_setup_interrupts -- + * + * Sets up the interrupts. + * + * Results: + * 0 if success, appropriate error code from bus_setup_intr otherwise. + * + * Side effects: + * Interrupt handler gets attached. + * + *------------------------------------------------------------------------------ + */ + +static int +vmci_setup_interrupts(struct vmci_softc *sc) +{ + struct vmci_interrupt *intr; + int error, flags; + + flags = INTR_TYPE_NET | INTR_MPSAFE; + if (sc->vmci_num_intr > 1) + flags |= INTR_EXCL; + + intr = &sc->vmci_intrs[0]; + error = bus_setup_intr(sc->vmci_dev, intr->vmci_irq, flags, NULL, + vmci_interrupt, NULL, &intr->vmci_handler); + if (error) + return (error); + bus_describe_intr(sc->vmci_dev, intr->vmci_irq, intr->vmci_handler, + "vmci_interrupt"); + + if (sc->vmci_num_intr == 2) { + intr = &sc->vmci_intrs[1]; + error = bus_setup_intr(sc->vmci_dev, intr->vmci_irq, flags, + NULL, vmci_interrupt_bm, NULL, &intr->vmci_handler); + if (error) + return (error); + bus_describe_intr(sc->vmci_dev, intr->vmci_irq, + intr->vmci_handler, "vmci_interrupt_bm"); + } + + return (0); +} + +/* + *------------------------------------------------------------------------------ + * + * vmci_interrupt -- + * + * Interrupt handler for legacy or MSI interrupt, or for first MSI-X + * interrupt (vector VMCI_INTR_DATAGRAM). + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static void +vmci_interrupt(void *arg) +{ + + if (vmci_sc->vmci_num_intr == 2) + taskqueue_enqueue(taskqueue_swi, + &vmci_sc->vmci_interrupt_dq_task); + else { + unsigned int icr; + + icr = inl(vmci_sc->vmci_ioaddr + VMCI_ICR_ADDR); + if (icr == 0 || icr == 0xffffffff) + return; + if (icr & VMCI_ICR_DATAGRAM) { *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201803250057.w2P0v0lA081039>