svn commit: r253014 - in projects/vmxnet/sys: dev/vmware/vmxnet3 modules/vmware modules/vmware/vmxnet3
Bryan Venteicher
bryanv at FreeBSD.org
Mon Jul 8 00:36:43 UTC 2013
Author: bryanv
Date: Mon Jul 8 00:36:42 2013
New Revision: 253014
URL: http://svnweb.freebsd.org/changeset/base/253014
Log:
Initial port of the OpenBSD vmxnet3 driver
Appears to have basic functionality, but a lot of work remains. Has
only been tested on QEMU's vmxnet3 backend.
Added:
projects/vmxnet/sys/dev/vmware/vmxnet3/
projects/vmxnet/sys/dev/vmware/vmxnet3/if_vmx.c (contents, props changed)
projects/vmxnet/sys/dev/vmware/vmxnet3/if_vmxreg.h (contents, props changed)
projects/vmxnet/sys/dev/vmware/vmxnet3/if_vmxvar.h (contents, props changed)
projects/vmxnet/sys/modules/vmware/vmxnet3/
projects/vmxnet/sys/modules/vmware/vmxnet3/Makefile (contents, props changed)
Modified:
projects/vmxnet/sys/modules/vmware/Makefile
Added: projects/vmxnet/sys/dev/vmware/vmxnet3/if_vmx.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/vmxnet/sys/dev/vmware/vmxnet3/if_vmx.c Mon Jul 8 00:36:42 2013 (r253014)
@@ -0,0 +1,2047 @@
+/*-
+ * Copyright (c) 2013 Tsubai Masanari
+ * Copyright (c) 2013 Bryan Venteicher <bryanv at FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: src/sys/dev/pci/if_vmx.c,v 1.11 2013/06/22 00:28:10 uebayasi Exp $
+ */
+
+/* Driver for VMware vmxnet3 virtual ethernet devices. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <net/bpf.h>
+
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include "if_vmxreg.h"
+#include "if_vmxvar.h"
+
+static int vmxnet3_probe(device_t);
+static int vmxnet3_attach(device_t);
+static int vmxnet3_detach(device_t);
+static int vmxnet3_shutdown(device_t);
+
+static int vmxnet3_alloc_resources(struct vmxnet3_softc *);
+static void vmxnet3_free_resources(struct vmxnet3_softc *);
+static int vmxnet3_query(struct vmxnet3_softc *);
+
+static int vmxnet3_alloc_shared_data(struct vmxnet3_softc *);
+static void vmxnet3_free_shared_data(struct vmxnet3_softc *);
+static int vmxnet3_alloc_txq_data(struct vmxnet3_softc *);
+static void vmxnet3_free_txq_data(struct vmxnet3_softc *);
+static int vmxnet3_alloc_rxq_data(struct vmxnet3_softc *);
+static void vmxnet3_free_rxq_data(struct vmxnet3_softc *);
+static int vmxnet3_alloc_queue_data(struct vmxnet3_softc *);
+static void vmxnet3_free_queue_data(struct vmxnet3_softc *);
+static int vmxnet3_alloc_mcast_table(struct vmxnet3_softc *);
+static void vmxnet3_init_shared_data(struct vmxnet3_softc *);
+static void vmxnet3_reinit_shared_data(struct vmxnet3_softc *);
+static int vmxnet3_alloc_data(struct vmxnet3_softc *);
+static void vmxnet3_free_data(struct vmxnet3_softc *);
+static int vmxnet3_setup_interface(struct vmxnet3_softc *);
+
+static void vmxnet3_evintr(struct vmxnet3_softc *);
+static void vmxnet3_txeof(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
+static void vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *, struct mbuf *);
+static int vmxnet3_newbuf(struct vmxnet3_softc *, struct vmxnet3_rxring *);
+static void vmxnet3_rxeof(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
+static void vmxnet3_intr(void *);
+
+static void vmxnet3_txstop(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
+static void vmxnet3_rxstop(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
+static void vmxnet3_stop(struct vmxnet3_softc *);
+
+static void vmxnet3_txinit(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
+static int vmxnet3_rxinit(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
+static int vmxnet3_reset_queues(struct vmxnet3_softc *);
+static void vmxnet3_init_locked(struct vmxnet3_softc *);
+static void vmxnet3_init(void *);
+
+static int vmxnet3_encap_offload_ctx(struct mbuf *, int *, int *, int *);
+static int vmxnet3_encap_load_mbuf(struct vmxnet3_softc *,
+ struct vmxnet3_txring *, struct mbuf **, bus_dmamap_t,
+ bus_dma_segment_t [], int *);
+static int vmxnet3_encap(struct vmxnet3_softc *, struct mbuf **);
+static void vmxnet3_start_locked(struct ifnet *);
+static void vmxnet3_start(struct ifnet *);
+
+static void vmxnet3_set_rxfilter(struct vmxnet3_softc *);
+static int vmxnet3_change_mtu(struct vmxnet3_softc *, int);
+static int vmxnet3_ioctl(struct ifnet *, u_long, caddr_t);
+
+static void vmxnet3_watchdog(struct vmxnet3_softc *);
+static void vmxnet3_tick(void *);
+static void vmxnet3_link_state(struct vmxnet3_softc *);
+static void vmxnet3_media_status(struct ifnet *, struct ifmediareq *);
+static int vmxnet3_media_change(struct ifnet *);
+static void vmxnet3_set_lladdr(struct vmxnet3_softc *);
+static void vmxnet3_get_lladdr(struct vmxnet3_softc *);
+
+static uint32_t vmxnet3_read_bar0(struct vmxnet3_softc *, bus_size_t);
+static void vmxnet3_write_bar0(struct vmxnet3_softc *, bus_size_t,
+ uint32_t);
+static uint32_t vmxnet3_read_bar1(struct vmxnet3_softc *, bus_size_t);
+static void vmxnet3_write_bar1(struct vmxnet3_softc *, bus_size_t,
+ uint32_t);
+static void vmxnet3_write_cmd(struct vmxnet3_softc *, uint32_t);
+static uint32_t vmxnet3_read_cmd(struct vmxnet3_softc *, uint32_t);
+
+static void vmxnet3_enable_intr(struct vmxnet3_softc *, int);
+static void vmxnet3_disable_intr(struct vmxnet3_softc *, int);
+static void vmxnet3_enable_all_intrs(struct vmxnet3_softc *);
+static void vmxnet3_disable_all_intrs(struct vmxnet3_softc *);
+
+static int vmxnet3_dma_malloc(struct vmxnet3_softc *, bus_size_t,
+ bus_size_t, struct vmxnet3_dma_alloc *);
+static void vmxnet3_dma_free(struct vmxnet3_softc *,
+ struct vmxnet3_dma_alloc *);
+
+static device_method_t vmxnet3_methods[] = {
+ /* Device interface. */
+ DEVMETHOD(device_probe, vmxnet3_probe),
+ DEVMETHOD(device_attach, vmxnet3_attach),
+ DEVMETHOD(device_detach, vmxnet3_detach),
+ DEVMETHOD(device_shutdown, vmxnet3_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t vmxnet3_driver = {
+ "vmx", vmxnet3_methods, sizeof(struct vmxnet3_softc)
+};
+
+static devclass_t vmxnet3_devclass;
+DRIVER_MODULE(vmx, pci, vmxnet3_driver, vmxnet3_devclass, 0, 0);
+
+MODULE_DEPEND(vmx, pci, 1, 1, 1);
+MODULE_DEPEND(vmx, ether, 1, 1, 1);
+
+#define VMXNET3_VMWARE_VENDOR_ID 0x15AD
+#define VMXNET3_VMWARE_DEVICE_ID 0x07B0
+
+static int
+vmxnet3_probe(device_t dev)
+{
+
+ if (pci_get_vendor(dev) == VMXNET3_VMWARE_VENDOR_ID &&
+ pci_get_device(dev) == VMXNET3_VMWARE_DEVICE_ID) {
+ device_set_desc(dev, "VMware VMXNET3 Ethernet Adapter");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+vmxnet3_attach(device_t dev)
+{
+ struct vmxnet3_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->vmx_dev = dev;
+
+ VMXNET3_CORE_LOCK_INIT(sc, device_get_nameunit(dev));
+ VMXNET3_RX_LOCK_INIT(sc, device_get_nameunit(dev));
+ VMXNET3_TX_LOCK_INIT(sc, device_get_nameunit(dev));
+ callout_init_mtx(&sc->vmx_tick, &sc->vmx_mtx, 0);
+
+ error = vmxnet3_alloc_resources(sc);
+ if (error)
+ goto fail;
+
+ error = vmxnet3_query(sc);
+ if (error)
+ goto fail;
+
+ error = vmxnet3_alloc_data(sc);
+ if (error)
+ goto fail;
+
+ error = vmxnet3_setup_interface(sc);
+ if (error)
+ goto fail;
+
+ error = bus_setup_intr(dev, sc->vmx_irq, INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, vmxnet3_intr, sc, &sc->vmx_intrhand);
+ if (error) {
+ ether_ifdetach(sc->vmx_ifp);
+ device_printf(dev, "could not set up interrupt\n");
+ goto fail;
+ }
+
+ vmxnet3_link_state(sc);
+
+fail:
+ if (error)
+ vmxnet3_detach(dev);
+
+ return (error);
+}
+
+static int
+vmxnet3_detach(device_t dev)
+{
+ struct vmxnet3_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ ifp = sc->vmx_ifp;
+
+ if (device_is_attached(dev)) {
+ ether_ifdetach(ifp);
+ VMXNET3_CORE_LOCK(sc);
+ vmxnet3_stop(sc);
+ VMXNET3_CORE_UNLOCK(sc);
+ callout_drain(&sc->vmx_tick);
+ }
+
+ if (sc->vmx_intrhand != NULL) {
+ bus_teardown_intr(dev, sc->vmx_irq, sc->vmx_intrhand);
+ sc->vmx_intrhand = NULL;
+ }
+
+ if (ifp != NULL) {
+ if_free(ifp);
+ sc->vmx_ifp = NULL;
+ }
+
+ if (sc->vmx_irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vmx_irq);
+ sc->vmx_irq = NULL;
+ }
+
+ vmxnet3_free_data(sc);
+ vmxnet3_free_resources(sc);
+
+ VMXNET3_CORE_LOCK_DESTROY(sc);
+ VMXNET3_TX_LOCK_DESTROY(sc);
+ VMXNET3_RX_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static int
+vmxnet3_shutdown(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+vmxnet3_alloc_resources(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ int rid;
+
+ dev = sc->vmx_dev;
+
+ rid = PCIR_BAR(0);
+ sc->vmx_res0 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->vmx_res0 == NULL) {
+ device_printf(dev,
+ "could not map BAR0 memory\n");
+ return (ENXIO);
+ }
+
+ sc->vmx_iot0 = rman_get_bustag(sc->vmx_res0);
+ sc->vmx_ioh0 = rman_get_bushandle(sc->vmx_res0);
+
+ rid = PCIR_BAR(1);
+ sc->vmx_res1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->vmx_res1 == NULL) {
+ device_printf(dev,
+ "could not map BAR1 memory\n");
+ return (ENXIO);
+ }
+
+ sc->vmx_iot1 = rman_get_bustag(sc->vmx_res1);
+ sc->vmx_ioh1 = rman_get_bushandle(sc->vmx_res1);
+
+ rid = 0;
+ sc->vmx_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->vmx_irq == NULL) {
+ device_printf(dev, "could not allocate interrupt resource\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+vmxnet3_free_resources(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ int rid;
+
+ dev = sc->vmx_dev;
+
+ if (sc->vmx_res0 != NULL) {
+ rid = PCIR_BAR(0);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->vmx_res0);
+ sc->vmx_res0 = NULL;
+ }
+
+ if (sc->vmx_res1 != NULL) {
+ rid = PCIR_BAR(1);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->vmx_res1);
+ sc->vmx_res1 = NULL;
+ }
+}
+
+static int
+vmxnet3_query(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ uint32_t version;
+
+ dev = sc->vmx_dev;
+
+ version = vmxnet3_read_bar1(sc, VMXNET3_BAR1_VRRS);
+ if ((version & 0x01) == 0) {
+ device_printf(dev, "unsupported hardware version %#x\n",
+ version);
+ return (ENOTSUP);
+ }
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_VRRS, 1);
+
+ version = vmxnet3_read_bar1(sc, VMXNET3_BAR1_UVRS);
+ if ((version & 0x01) == 0) {
+ device_printf(dev, "unsupported UPT version %#x\n", version);
+ return (ENOTSUP);
+ }
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_UVRS, 1);
+
+ vmxnet3_get_lladdr(sc);
+
+ return (0);
+}
+
+static int
+vmxnet3_alloc_shared_data(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ uint8_t *kva;
+ size_t size;
+ int i, error;
+
+ dev = sc->vmx_dev;
+
+ size = sizeof(struct vmxnet3_driver_shared);
+ error = vmxnet3_dma_malloc(sc, size, 1, &sc->vmx_ds_dma);
+ if (error) {
+ device_printf(dev, "cannot alloc shared memory\n");
+ return (error);
+ }
+ sc->vmx_ds = (struct vmxnet3_driver_shared *) sc->vmx_ds_dma.dma_vaddr;
+
+ size = VMXNET3_TX_QUEUES * sizeof(struct vmxnet3_txq_shared);
+ size += VMXNET3_RX_QUEUES * sizeof(struct vmxnet3_rxq_shared);
+ error = vmxnet3_dma_malloc(sc, size, 128, &sc->vmx_qs_dma);
+ if (error) {
+ device_printf(dev, "cannot alloc queue shared memory\n");
+ return (error);
+ }
+ sc->vmx_qs = (void *) sc->vmx_qs_dma.dma_vaddr;
+ kva = sc->vmx_qs;
+
+ for (i = 0; i < VMXNET3_TX_QUEUES; i++) {
+ sc->vmx_txq[i].vxtxq_ts = (struct vmxnet3_txq_shared *) kva;
+ kva += sizeof(struct vmxnet3_txq_shared);
+ }
+ for (i = 0; i < VMXNET3_RX_QUEUES; i++) {
+ sc->vmx_rxq[i].vxrxq_rs = (struct vmxnet3_rxq_shared *) kva;
+ kva += sizeof(struct vmxnet3_rxq_shared);
+ }
+
+ return (0);
+}
+
+static void
+vmxnet3_free_shared_data(struct vmxnet3_softc *sc)
+{
+
+ if (sc->vmx_qs != NULL) {
+ vmxnet3_dma_free(sc, &sc->vmx_qs_dma);
+ sc->vmx_qs = NULL;
+ }
+
+ if (sc->vmx_ds != NULL) {
+ vmxnet3_dma_free(sc, &sc->vmx_ds_dma);
+ sc->vmx_ds = NULL;
+ }
+}
+
+static int
+vmxnet3_alloc_txq_data(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct vmxnet3_txqueue *txq;
+ struct vmxnet3_txring *txr;
+ struct vmxnet3_comp_ring *txc;
+ size_t descsz, compsz;
+ int i, q, error;
+
+ dev = sc->vmx_dev;
+ descsz = VMXNET3_TX_NDESC * sizeof(struct vmxnet3_txdesc);
+ compsz = VMXNET3_TX_NCOMPDESC * sizeof(struct vmxnet3_txcompdesc);
+
+ for (q = 0; q < VMXNET3_TX_QUEUES; q++) {
+ txq = &sc->vmx_txq[q];
+ txr = &txq->vxtxq_cmd_ring;
+ txc = &txq->vxtxq_comp_ring;
+
+ error = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ VMXNET3_TSO_MAXSIZE, /* maxsize */
+ VMXNET3_TX_MAXSEGS, /* nsegments */
+ PAGE_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &txr->vxtxr_txtag);
+ if (error) {
+ device_printf(dev,
+ "unable to create Tx buffer tag for queue %d\n", q);
+ return (error);
+ }
+
+ error = vmxnet3_dma_malloc(sc, descsz, 512, &txr->vxtxr_dma);
+ if (error) {
+ device_printf(dev, "cannot alloc Tx descriptors for "
+ "queue %d error %d\n", q, error);
+ return (error);
+ }
+ txr->vxtxr_txd =
+ (struct vmxnet3_txdesc *) txr->vxtxr_dma.dma_vaddr;
+
+ error = vmxnet3_dma_malloc(sc, compsz, 512, &txc->vxcr_dma);
+ if (error) {
+ device_printf(dev, "cannot alloc Tx comp descriptors "
+ "for queue %d error %d\n", q, error);
+ return (error);
+ }
+ txc->vxcr_u.txcd =
+ (struct vmxnet3_txcompdesc *) txc->vxcr_dma.dma_vaddr;
+
+ for (i = 0; i < VMXNET3_TX_NDESC; i++) {
+ error = bus_dmamap_create(txr->vxtxr_txtag, 0,
+ &txr->vxtxr_dmap[i]);
+ if (error) {
+ device_printf(dev, "unable to create Tx buf "
+ "dmamap for queue %d idx %d\n", q, i);
+ return (error);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static void
+vmxnet3_free_txq_data(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct vmxnet3_txqueue *txq;
+ struct vmxnet3_txring *txr;
+ struct vmxnet3_comp_ring *txc;
+ int i, q;
+
+ dev = sc->vmx_dev;
+
+ for (q = 0; q < VMXNET3_TX_QUEUES; q++) {
+ txq = &sc->vmx_txq[q];
+ txr = &txq->vxtxq_cmd_ring;
+ txc = &txq->vxtxq_comp_ring;
+
+ for (i = 0; i < VMXNET3_TX_NDESC; i++) {
+ if (txr->vxtxr_dmap[i] != NULL) {
+ bus_dmamap_destroy(txr->vxtxr_txtag,
+ txr->vxtxr_dmap[i]);
+ txr->vxtxr_dmap[i] = NULL;
+ }
+ }
+
+ if (txc->vxcr_u.txcd != NULL) {
+ vmxnet3_dma_free(sc, &txc->vxcr_dma);
+ txc->vxcr_u.txcd = NULL;
+ }
+
+ if (txr->vxtxr_txd != NULL) {
+ vmxnet3_dma_free(sc, &txr->vxtxr_dma);
+ txr->vxtxr_txd = NULL;
+ }
+
+ if (txr->vxtxr_txtag != NULL) {
+ bus_dma_tag_destroy(txr->vxtxr_txtag);
+ txr->vxtxr_txtag = NULL;
+ }
+ }
+}
+
+static int
+vmxnet3_alloc_rxq_data(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct vmxnet3_rxqueue *rxq;
+ struct vmxnet3_rxring *rxr;
+ struct vmxnet3_comp_ring *rxc;
+ int descsz, compsz;
+ int i, j, q, error;
+
+ dev = sc->vmx_dev;
+ descsz = VMXNET3_RX_NDESC * sizeof(struct vmxnet3_rxdesc);
+ compsz = VMXNET3_RX_NCOMPDESC * sizeof(struct vmxnet3_rxcompdesc);
+
+ for (q = 0; q < VMXNET3_RX_QUEUES; q++) {
+ rxq = &sc->vmx_rxq[q];
+ rxc = &rxq->vxrxq_comp_ring;
+
+ for (i = 0; i < 2; i++) {
+ rxr = &rxq->vxrxq_cmd_ring[i];
+
+ error = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MJUMPAGESIZE, /* maxsize */
+ 1, /* nsegments */
+ MJUMPAGESIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &rxr->vxrxr_rxtag);
+ if (error) {
+ device_printf(dev,
+ "unable to create Rx buffer tag for queue %d\n", q);
+ return (error);
+ }
+
+ error = vmxnet3_dma_malloc(sc, descsz, 512,
+ &rxr->vxrxr_dma);
+ if (error) {
+ device_printf(dev, "cannot allocate Rx "
+ "descriptors for queue %d/%d error %d\n",
+ i, q, error);
+ return (error);
+ }
+ rxr->vxrxr_rxd =
+ (struct vmxnet3_rxdesc *) rxr->vxrxr_dma.dma_vaddr;
+ }
+
+ error = vmxnet3_dma_malloc(sc, compsz, 512, &rxc->vxcr_dma);
+ if (error) {
+ device_printf(dev, "cannot alloc Rx comp descriptors "
+ "for queue %d error %d\n", q, error);
+ return (error);
+ }
+ rxc->vxcr_u.rxcd =
+ (struct vmxnet3_rxcompdesc *) rxc->vxcr_dma.dma_vaddr;
+
+ for (i = 0; i < 2; i++) {
+ rxr = &rxq->vxrxq_cmd_ring[i];
+
+ error = bus_dmamap_create(rxr->vxrxr_rxtag, 0,
+ &rxr->vxrxr_spare_dmap);
+ if (error) {
+ device_printf(dev, "unable to create spare "
+ "dmamap for queue %d/%d error %d\n",
+ q, i, error);
+ return (error);
+ }
+
+ for (j = 0; j < VMXNET3_RX_NDESC; j++) {
+ error = bus_dmamap_create(rxr->vxrxr_rxtag, 0,
+ &rxr->vxrxr_dmap[j]);
+ if (error) {
+ device_printf(dev, "unable to create "
+ "dmamap for queue %d/%d slot %d "
+ "error %d\n",
+ q, i, j, error);
+ return (error);
+ }
+ }
+ }
+ }
+
+ return (0);
+}
+
+static void
+vmxnet3_free_rxq_data(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct vmxnet3_rxqueue *rxq;
+ struct vmxnet3_rxring *rxr;
+ struct vmxnet3_comp_ring *rxc;
+ int i, j, q;
+
+ dev = sc->vmx_dev;
+
+ for (q = 0; q < VMXNET3_RX_QUEUES; q++) {
+ rxq = &sc->vmx_rxq[q];
+ rxc = &rxq->vxrxq_comp_ring;
+
+ for (i = 0; i < 2; i++) {
+ rxr = &rxq->vxrxq_cmd_ring[i];
+
+ if (rxr->vxrxr_spare_dmap != NULL) {
+ bus_dmamap_destroy(rxr->vxrxr_rxtag,
+ rxr->vxrxr_spare_dmap);
+ rxr->vxrxr_spare_dmap = NULL;
+ }
+
+ for (j = 0; j < VMXNET3_RX_NDESC; j++) {
+ if (rxr->vxrxr_dmap[j] != NULL) {
+ bus_dmamap_destroy(rxr->vxrxr_rxtag,
+ rxr->vxrxr_dmap[j]);
+ rxr->vxrxr_dmap[j] = NULL;
+ }
+ }
+ }
+
+ if (rxc->vxcr_u.rxcd != NULL) {
+ vmxnet3_dma_free(sc, &rxc->vxcr_dma);
+ rxc->vxcr_u.rxcd = NULL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ rxr = &rxq->vxrxq_cmd_ring[i];
+
+ if (rxr->vxrxr_rxd != NULL) {
+ vmxnet3_dma_free(sc, &rxr->vxrxr_dma);
+ rxr->vxrxr_rxd = NULL;
+ }
+
+ if (rxr->vxrxr_rxtag != NULL) {
+ bus_dma_tag_destroy(rxr->vxrxr_rxtag);
+ rxr->vxrxr_rxtag = NULL;
+ }
+ }
+ }
+}
+
+static int
+vmxnet3_alloc_queue_data(struct vmxnet3_softc *sc)
+{
+ int error;
+
+ error = vmxnet3_alloc_txq_data(sc);
+ if (error)
+ return (error);
+
+ error = vmxnet3_alloc_rxq_data(sc);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static void
+vmxnet3_free_queue_data(struct vmxnet3_softc *sc)
+{
+
+ vmxnet3_free_rxq_data(sc);
+ vmxnet3_free_txq_data(sc);
+}
+
+static int
+vmxnet3_alloc_mcast_table(struct vmxnet3_softc *sc)
+{
+ int error;
+
+ error = vmxnet3_dma_malloc(sc, VMXNET3_MULTICAST_MAX * ETHER_ADDR_LEN,
+ 32, &sc->vmx_mcast_dma);
+ if (error)
+ device_printf(sc->vmx_dev, "unable to alloc multicast table\n");
+ else
+ sc->vmx_mcast = sc->vmx_mcast_dma.dma_vaddr;
+
+ return (error);
+}
+
+static void
+vmxnet3_free_mcast_table(struct vmxnet3_softc *sc)
+{
+
+ if (sc->vmx_mcast != NULL) {
+ vmxnet3_dma_free(sc, &sc->vmx_mcast_dma);
+ sc->vmx_mcast = NULL;
+ }
+}
+
+static void
+vmxnet3_init_shared_data(struct vmxnet3_softc *sc)
+{
+ struct vmxnet3_driver_shared *ds;
+ struct vmxnet3_txqueue *txq;
+ struct vmxnet3_txq_shared *txs;
+ struct vmxnet3_rxqueue *rxq;
+ struct vmxnet3_rxq_shared *rxs;
+ u_int major, minor, release_code, rev;
+ int i;
+
+ ds = sc->vmx_ds;
+ ds->magic = VMXNET3_REV1_MAGIC;
+ ds->version = VMXNET3_DRIVER_VERSION;
+
+ major = __FreeBSD_version / 100000;
+ minor = (__FreeBSD_version / 1000) % 100;
+ release_code = (__FreeBSD_version / 100) % 10;
+ rev = __FreeBSD_version % 100;
+ ds->guest = release_code << 30 | rev << 22 | major << 14 | minor << 6 |
+ VMXNET3_GOS_FREEBSD;
+#ifdef __LP64__
+ ds->guest |= VMXNET3_GOS_64BIT;
+#else
+ ds->guest |= VMXNET3_GOS_32BIT;
+#endif
+
+ ds->vmxnet3_revision = 1;
+ ds->upt_version = 1;
+ ds->upt_features = UPT1_F_CSUM | UPT1_F_VLAN;
+ ds->driver_data = vtophys(sc);
+ ds->driver_data_len = sizeof(struct vmxnet3_softc);
+ ds->queue_shared = sc->vmx_qs_dma.dma_paddr;
+ ds->queue_shared_len = sc->vmx_qs_dma.dma_size;
+ ds->mtu = ETHERMTU;
+ ds->ntxqueue = VMXNET3_TX_QUEUES;
+ ds->nrxqueue = VMXNET3_RX_QUEUES;
+ ds->mcast_table = sc->vmx_mcast_dma.dma_paddr;
+ ds->mcast_tablelen = sc->vmx_mcast_dma.dma_size;
+ ds->automask = 1;
+ ds->nintr = VMXNET3_NINTR;
+ ds->evintr = 0;
+ ds->ictrl = VMXNET3_ICTRL_DISABLE_ALL;
+ for (i = 0; i < VMXNET3_NINTR; i++)
+ ds->modlevel[i] = UPT1_IMOD_ADAPTIVE;
+
+ for (i = 0; i < VMXNET3_TX_QUEUES; i++) {
+ txq = &sc->vmx_txq[i];
+ txs = txq->vxtxq_ts;
+
+ txs->npending = 0;
+ txs->intr_threshold = 1;
+ txs->cmd_ring = txq->vxtxq_cmd_ring.vxtxr_dma.dma_paddr;
+ txs->cmd_ring_len = VMXNET3_TX_NDESC;
+ txs->comp_ring = txq->vxtxq_comp_ring.vxcr_dma.dma_paddr;
+ txs->comp_ring_len = VMXNET3_TX_NCOMPDESC;
+ txs->driver_data = vtophys(txq);
+ txs->driver_data_len = sizeof(struct vmxnet3_txqueue);
+ txs->intr_idx = 0;
+ txs->stopped = 1;
+ txs->error = 0;
+ }
+
+ for (i = 0; i < VMXNET3_RX_QUEUES; i++) {
+ rxq = &sc->vmx_rxq[i];
+ rxs = rxq->vxrxq_rs;
+
+ rxs->cmd_ring[0] = rxq->vxrxq_cmd_ring[0].vxrxr_dma.dma_paddr;
+ rxs->cmd_ring_len[0] = VMXNET3_RX_NDESC;
+ rxs->cmd_ring[1] = rxq->vxrxq_cmd_ring[1].vxrxr_dma.dma_paddr;
+ rxs->cmd_ring_len[1] = VMXNET3_RX_NDESC;
+ rxs->comp_ring = rxq->vxrxq_comp_ring.vxcr_dma.dma_paddr;
+ rxs->comp_ring_len = VMXNET3_RX_NCOMPDESC;
+ rxs->driver_data = vtophys(rxq);
+ rxs->driver_data_len = sizeof(struct vmxnet3_rxqueue);
+ rxs->intr_idx = 0;
+ rxs->stopped = 1;
+ rxs->error = 0;
+ }
+
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSL, sc->vmx_ds_dma.dma_paddr);
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSH,
+ sc->vmx_ds_dma.dma_paddr >> 32);
+}
+
+static void
+vmxnet3_reinit_shared_data(struct vmxnet3_softc *sc)
+{
+
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSL, sc->vmx_ds_dma.dma_paddr);
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSH,
+ sc->vmx_ds_dma.dma_paddr >> 32);
+}
+
+static int
+vmxnet3_alloc_data(struct vmxnet3_softc *sc)
+{
+ int error;
+
+ error = vmxnet3_alloc_shared_data(sc);
+ if (error)
+ return (error);
+
+ error = vmxnet3_alloc_queue_data(sc);
+ if (error)
+ return (error);
+
+ error = vmxnet3_alloc_mcast_table(sc);
+ if (error)
+ return (error);
+
+ vmxnet3_init_shared_data(sc);
+
+ return (0);
+}
+
+static void
+vmxnet3_free_data(struct vmxnet3_softc *sc)
+{
+
+ vmxnet3_free_mcast_table(sc);
+ vmxnet3_free_queue_data(sc);
+ vmxnet3_free_shared_data(sc);
+}
+
+static int
+vmxnet3_setup_interface(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+
+ dev = sc->vmx_dev;
+
+ ifp = sc->vmx_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "cannot allocate ifnet structure\n");
+ return (ENOSPC);
+ }
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ if_initbaudrate(ifp, IF_Gbps(10)); /* Approx. */
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = vmxnet3_init;
+ ifp->if_ioctl = vmxnet3_ioctl;
+ ifp->if_start = vmxnet3_start;
+ ifp->if_snd.ifq_drv_maxlen = VMXNET3_TX_NDESC - 1;
+ IFQ_SET_MAXLEN(&ifp->if_snd, VMXNET3_TX_NDESC - 1);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ether_ifattach(ifp, sc->vmx_lladdr);
+
+ if (sc->vmx_ds->upt_features & UPT1_F_VLAN)
+ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING;
+ if (sc->vmx_ds->upt_features & UPT1_F_CSUM) {
+ ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_TXCSUM;
+ ifp->if_hwassist |= VMXNET3_CSUM_FEATURES;
+ }
+
+ ifp->if_capenable = ifp->if_capabilities;
+
+ ifmedia_init(&sc->vmx_media, 0, vmxnet3_media_change,
+ vmxnet3_media_status);
+ ifmedia_add(&sc->vmx_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->vmx_media, IFM_ETHER | IFM_AUTO);
+
+ return (0);
+}
+
+static void
+vmxnet3_evintr(struct vmxnet3_softc *sc)
+{
+ device_t dev;
+ struct ifnet *ifp;
+ struct vmxnet3_txq_shared *ts;
+ struct vmxnet3_rxq_shared *rs;
+ uint32_t event;
+ int reset;
+
+ dev = sc->vmx_dev;
+ ifp = sc->vmx_ifp;
+ event = sc->vmx_ds->event;
+ reset = 0;
+
+ /* Clear events. */
+ vmxnet3_write_bar1(sc, VMXNET3_BAR1_EVENT, event);
+
+ if (event & VMXNET3_EVENT_LINK)
+ vmxnet3_link_state(sc);
+
+ if (event & (VMXNET3_EVENT_TQERROR | VMXNET3_EVENT_RQERROR)) {
+ reset = 1;
+ vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_STATUS);
+ ts = sc->vmx_txq[0].vxtxq_ts;
+ if (ts->stopped != 0)
+ device_printf(dev, "TX queue error %#x\n", ts->error);
+ rs = sc->vmx_rxq[0].vxrxq_rs;
+ if (rs->stopped != 0)
+ device_printf(dev, "RX queue error %#x\n", rs->error);
+ }
+
+ if (event & VMXNET3_EVENT_DIC)
+ device_printf(dev, "device implementation change event\n");
+ if (event & VMXNET3_EVENT_DEBUG)
+ device_printf(dev, "debug event\n");
+
+ if (reset != 0) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ vmxnet3_init(sc);
+ }
+}
+
+static void
+vmxnet3_txeof(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *txq)
+{
+ struct ifnet *ifp;
+ struct vmxnet3_txring *txr;
+ struct vmxnet3_comp_ring *txc;
+ struct vmxnet3_txcompdesc *txcd;
+ u_int sop;
+
+ ifp = sc->vmx_ifp;
+ txr = &txq->vxtxq_cmd_ring;
+ txc = &txq->vxtxq_comp_ring;
+
+ VMXNET3_TX_LOCK_ASSERT(sc);
+
+ for (;;) {
+ txcd = &txc->vxcr_u.txcd[txc->vxcr_next];
+ if (txcd->gen != txc->vxcr_gen)
+ break;
+
+ if (++txc->vxcr_next == VMXNET3_TX_NCOMPDESC) {
+ txc->vxcr_next = 0;
+ txc->vxcr_gen ^= 1;
+ }
+
+ sop = txr->vxtxr_next;
+ if (txr->vxtxr_m[sop] != NULL) {
+ bus_dmamap_sync(txr->vxtxr_txtag, txr->vxtxr_dmap[sop],
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txr->vxtxr_txtag,
+ txr->vxtxr_dmap[sop]);
+
+ m_freem(txr->vxtxr_m[sop]);
+ txr->vxtxr_m[sop] = NULL;
+
+ ifp->if_opackets++;
+ }
+
+ txr->vxtxr_next = (txcd->eop_idx + 1) % VMXNET3_TX_NDESC;
+ }
+
+ if (txr->vxtxr_head == txr->vxtxr_next)
+ sc->vmx_watchdog_timer = 0;
+}
+
+static void
+vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m)
+{
+
+ if (rxcd->ipv4 && rxcd->ipcsum_ok)
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID;
+ if (rxcd->fragment)
+ return;
+ if (rxcd->csum_ok && (rxcd->tcp || rxcd->udp)) {
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xFFFF;
+ }
+}
+
+static int
+vmxnet3_newbuf(struct vmxnet3_softc *sc, struct vmxnet3_rxring *rxr)
+{
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct vmxnet3_rxdesc *rxd;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list