svn commit: r331510 - in head: share/man/man4 sys/conf sys/dev/vmware/vmci sys/modules/vmware sys/modules/vmware/vmci

Mark Peek mp at FreeBSD.org
Sun Mar 25 00:57:01 UTC 2018


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 at 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 at 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 ***


More information about the svn-src-all mailing list