Module Name:    src
Committed By:   hikaru
Date:           Fri Nov 25 05:29:55 UTC 2016

Modified Files:
        src/sys/arch/x86/pci: if_vmx.c if_vmxreg.h

Log Message:
Sync code with FreeBSD to support RSS

- Use MSI/MSI-X if it is available.
- Support TSO.

co-authored by k-nakahara


To generate a diff of this commit:
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/x86/pci/if_vmx.c
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/x86/pci/if_vmxreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/x86/pci/if_vmx.c
diff -u src/sys/arch/x86/pci/if_vmx.c:1.7 src/sys/arch/x86/pci/if_vmx.c:1.8
--- src/sys/arch/x86/pci/if_vmx.c:1.7	Fri Jun 10 13:27:13 2016
+++ src/sys/arch/x86/pci/if_vmx.c	Fri Nov 25 05:29:54 2016
@@ -1,8 +1,9 @@
-/*	$NetBSD: if_vmx.c,v 1.7 2016/06/10 13:27:13 ozaki-r Exp $	*/
+/*	$NetBSD: if_vmx.c,v 1.8 2016/11/25 05:29:54 hikaru Exp $	*/
 /*	$OpenBSD: if_vmx.c,v 1.16 2014/01/22 06:04:17 brad Exp $	*/
 
 /*
  * Copyright (c) 2013 Tsubai Masanari
+ * Copyright (c) 2013 Bryan Venteicher <bry...@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
@@ -18,9 +19,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vmx.c,v 1.7 2016/06/10 13:27:13 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vmx.c,v 1.8 2016/11/25 05:29:54 hikaru Exp $");
 
 #include <sys/param.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
 #include <sys/bus.h>
 #include <sys/device.h>
 #include <sys/mbuf.h>
@@ -35,6 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vmx.c,v 1
 #include <netinet/in_systm.h>	/* for <netinet/ip.h> */
 #include <netinet/in.h>		/* for <netinet/ip.h> */
 #include <netinet/ip.h>		/* for struct ip */
+#include <netinet/ip6.h>	/* for struct ip6_hdr */
 #include <netinet/tcp.h>	/* for struct tcphdr */
 #include <netinet/udp.h>	/* for struct udphdr */
 
@@ -44,137 +49,446 @@ __KERNEL_RCSID(0, "$NetBSD: if_vmx.c,v 1
 
 #include <arch/x86/pci/if_vmxreg.h>
 
-#define NRXQUEUE 1
-#define NTXQUEUE 1
+#define VMXNET3_DRIVER_VERSION 0x00010000
 
-#define NTXDESC 128 /* tx ring size */
-#define NTXSEGS 8 /* tx descriptors per packet */
-#define NRXDESC 128
-#define NTXCOMPDESC NTXDESC
-#define NRXCOMPDESC (NRXDESC * 2)	/* ring1 + ring2 */
+/*
+ * Max descriptors per Tx packet. We must limit the size of the
+ * any TSO packets based on the number of segments.
+ */
+#define VMXNET3_TX_MAXSEGS		32
+#define VMXNET3_TX_MAXSIZE		(VMXNET3_TX_MAXSEGS * MCLBYTES)
 
-#define VMXNET3_DRIVER_VERSION 0x00010000
+/*
+ * Maximum support Tx segments size. The length field in the
+ * Tx descriptor is 14 bits.
+ */
+#define VMXNET3_TX_MAXSEGSIZE		(1 << 14)
+
+/*
+ * The maximum number of Rx segments we accept.
+ */
+#define VMXNET3_MAX_RX_SEGS		0	/* no segments */
+
+/*
+ * Predetermined size of the multicast MACs filter table. If the
+ * number of multicast addresses exceeds this size, then the
+ * ALL_MULTI mode is use instead.
+ */
+#define VMXNET3_MULTICAST_MAX		32
+
+/*
+ * Our Tx watchdog timeout.
+ */
+#define VMXNET3_WATCHDOG_TIMEOUT	5
+
+/*
+ * IP protocols that we can perform Tx checksum offloading of.
+ */
+#define VMXNET3_CSUM_OFFLOAD \
+    (M_CSUM_TCPv4 | M_CSUM_UDPv4)
+#define VMXNET3_CSUM_OFFLOAD_IPV6 \
+    (M_CSUM_TCPv6 | M_CSUM_UDPv6)
+
+#define VMXNET3_CSUM_ALL_OFFLOAD \
+    (VMXNET3_CSUM_OFFLOAD | VMXNET3_CSUM_OFFLOAD_IPV6 | M_CSUM_TSOv4 | M_CSUM_TSOv6)
+
+#define VMXNET3_RXRINGS_PERQ 2
+
+#define VMXNET3_CORE_LOCK(_sc)		mutex_enter((_sc)->vmx_mtx)
+#define VMXNET3_CORE_UNLOCK(_sc)	mutex_exit((_sc)->vmx_mtx)
+#define VMXNET3_CORE_LOCK_ASSERT(_sc)	mutex_owned((_sc)->vmx_mtx)
+#define VMXNET3_CORE_LOCK_ASSERT_NOTOWNED(_sc) \
+    (!mutex_owned((_sc)->vmx_mtx))
+
+#define VMXNET3_RXQ_LOCK(_rxq)		mutex_enter((_rxq)->vxrxq_mtx)
+#define VMXNET3_RXQ_UNLOCK(_rxq)	mutex_exit((_rxq)->vxrxq_mtx)
+#define VMXNET3_RXQ_LOCK_ASSERT(_rxq)		\
+    mutex_owned((_rxq)->vxrxq_mtx)
+#define VMXNET3_RXQ_LOCK_ASSERT_NOTOWNED(_rxq)	\
+    (!mutex_owned((_rxq)->vxrxq_mtx))
+
+#define VMXNET3_TXQ_LOCK(_txq)		mutex_enter((_txq)->vxtxq_mtx)
+#define VMXNET3_TXQ_UNLOCK(_txq)	mutex_exit((_txq)->vxtxq_mtx)
+#define VMXNET3_TXQ_LOCK_ASSERT(_txq)		\
+    mutex_owned((_txq)->vxtxq_mtx)
+#define VMXNET3_TXQ_LOCK_ASSERT_NOTOWNED(_txq)	\
+    (!mutex_owned((_txq)->vxtxq_mtx))
+
+struct vmxnet3_dma_alloc {
+	bus_addr_t dma_paddr;
+	void *dma_vaddr;
+	bus_dmamap_t dma_map;
+	bus_size_t dma_size;
+	bus_dma_segment_t dma_segs[1];
+};
+
+struct vmxnet3_txbuf {
+	bus_dmamap_t vtxb_dmamap;
+	struct mbuf *vtxb_m;
+};
 
 struct vmxnet3_txring {
-	struct mbuf *m[NTXDESC];
-	bus_dmamap_t dmap[NTXDESC];
-	struct vmxnet3_txdesc *txd;
-	u_int head;
-	u_int next;
-	uint8_t gen;
+	struct vmxnet3_txbuf *vxtxr_txbuf;
+	struct vmxnet3_txdesc *vxtxr_txd;
+	u_int vxtxr_head;
+	u_int vxtxr_next;
+	u_int vxtxr_ndesc;
+	int vxtxr_gen;
+	struct vmxnet3_dma_alloc vxtxr_dma;
+};
+
+struct vmxnet3_rxbuf {
+	bus_dmamap_t vrxb_dmamap;
+	struct mbuf *vrxb_m;
 };
 
 struct vmxnet3_rxring {
-	struct mbuf *m[NRXDESC];
-	bus_dmamap_t dmap[NRXDESC];
-	struct vmxnet3_rxdesc *rxd;
-	u_int fill;
-	uint8_t gen;
-	uint8_t rid;
+	struct vmxnet3_rxbuf *vxrxr_rxbuf;
+	struct vmxnet3_rxdesc *vxrxr_rxd;
+	u_int vxrxr_fill;
+	u_int vxrxr_ndesc;
+	int vxrxr_gen;
+	int vxrxr_rid;
+	struct vmxnet3_dma_alloc vxrxr_dma;
+	bus_dmamap_t vxrxr_spare_dmap;
 };
 
 struct vmxnet3_comp_ring {
 	union {
 		struct vmxnet3_txcompdesc *txcd;
 		struct vmxnet3_rxcompdesc *rxcd;
-	};
-	u_int next;
-	uint8_t gen;
+	} vxcr_u;
+	u_int vxcr_next;
+	u_int vxcr_ndesc;
+	int vxcr_gen;
+	struct vmxnet3_dma_alloc vxcr_dma;
+};
+
+struct vmxnet3_txq_stats {
+	uint64_t vmtxs_opackets;	/* if_opackets */
+	uint64_t vmtxs_obytes;		/* if_obytes */
+	uint64_t vmtxs_omcasts;		/* if_omcasts */
+	uint64_t vmtxs_csum;
+	uint64_t vmtxs_tso;
+	uint64_t vmtxs_full;
+	uint64_t vmtxs_offload_failed;
 };
 
 struct vmxnet3_txqueue {
-	struct vmxnet3_txring cmd_ring;
-	struct vmxnet3_comp_ring comp_ring;
-	struct vmxnet3_txq_shared *ts;
+	kmutex_t *vxtxq_mtx;
+	struct vmxnet3_softc *vxtxq_sc;
+	int vxtxq_id;
+	int vxtxq_intr_idx;
+	int vxtxq_watchdog;
+	struct vmxnet3_txring vxtxq_cmd_ring;
+	struct vmxnet3_comp_ring vxtxq_comp_ring;
+	struct vmxnet3_txq_stats vxtxq_stats;
+	struct vmxnet3_txq_shared *vxtxq_ts;
+	char vxtxq_name[16];
+};
+
+struct vmxnet3_rxq_stats {
+	uint64_t vmrxs_ipackets;	/* if_ipackets */
+	uint64_t vmrxs_ibytes;		/* if_ibytes */
+	uint64_t vmrxs_iqdrops;		/* if_iqdrops */
+	uint64_t vmrxs_ierrors;		/* if_ierrors */
 };
 
 struct vmxnet3_rxqueue {
-	struct vmxnet3_rxring cmd_ring[2];
-	struct vmxnet3_comp_ring comp_ring;
-	struct vmxnet3_rxq_shared *rs;
+	kmutex_t *vxrxq_mtx;
+	struct vmxnet3_softc *vxrxq_sc;
+	int vxrxq_id;
+	int vxrxq_intr_idx;
+	struct mbuf *vxrxq_mhead;
+	struct mbuf *vxrxq_mtail;
+	struct vmxnet3_rxring vxrxq_cmd_ring[VMXNET3_RXRINGS_PERQ];
+	struct vmxnet3_comp_ring vxrxq_comp_ring;
+	struct vmxnet3_rxq_stats vxrxq_stats;
+	struct vmxnet3_rxq_shared *vxrxq_rs;
+	char vxrxq_name[16];
+};
+
+struct vmxnet3_statistics {
+	uint32_t vmst_defragged;
+	uint32_t vmst_defrag_failed;
+	uint32_t vmst_mgetcl_failed;
+	uint32_t vmst_mbuf_load_failed;
 };
 
 struct vmxnet3_softc {
-	device_t sc_dev;
-	struct ethercom sc_ethercom;
-	struct ifmedia sc_media;
-
-	bus_space_tag_t	sc_iot0;
-	bus_space_tag_t	sc_iot1;
-	bus_space_handle_t sc_ioh0;
-	bus_space_handle_t sc_ioh1;
-	bus_dma_tag_t sc_dmat;
-
-	struct vmxnet3_txqueue sc_txq[NTXQUEUE];
-	struct vmxnet3_rxqueue sc_rxq[NRXQUEUE];
-	struct vmxnet3_driver_shared *sc_ds;
-	uint8_t *sc_mcast;
+	device_t vmx_dev;
+	struct ethercom vmx_ethercom;
+	struct ifmedia vmx_media;
+	struct vmxnet3_driver_shared *vmx_ds;
+	int vmx_flags;
+#define VMXNET3_FLAG_NO_MSIX	(1 << 0)
+#define VMXNET3_FLAG_RSS	(1 << 1)
+#define VMXNET3_FLAG_ATTACHED	(1 << 2)
+
+	struct vmxnet3_txqueue *vmx_txq;
+	struct vmxnet3_rxqueue *vmx_rxq;
+
+	struct pci_attach_args *vmx_pa;
+
+	bus_space_tag_t vmx_iot0;
+	bus_space_tag_t vmx_iot1;
+	bus_space_handle_t vmx_ioh0;
+	bus_space_handle_t vmx_ioh1;
+	bus_size_t vmx_ios0;
+	bus_size_t vmx_ios1;
+	bus_dma_tag_t vmx_dmat;
+
+	int vmx_link_active;
+	int vmx_ntxqueues;
+	int vmx_nrxqueues;
+	int vmx_ntxdescs;
+	int vmx_nrxdescs;
+	int vmx_max_rxsegs;
+
+	struct vmxnet3_statistics vmx_stats;
+
+	int vmx_intr_type;
+	int vmx_intr_mask_mode;
+	int vmx_event_intr_idx;
+	int vmx_nintrs;
+	pci_intr_handle_t *vmx_intrs;	/* legacy use vmx_intrs[0] */
+	void *vmx_ihs[VMXNET3_MAX_INTRS];
+
+	kmutex_t *vmx_mtx;
+
+	uint8_t *vmx_mcast;
+	void *vmx_qs;
+	struct vmxnet3_rss_shared *vmx_rss;
+	callout_t vmx_tick;
+	struct vmxnet3_dma_alloc vmx_ds_dma;
+	struct vmxnet3_dma_alloc vmx_qs_dma;
+	struct vmxnet3_dma_alloc vmx_mcast_dma;
+	struct vmxnet3_dma_alloc vmx_rss_dma;
+	int vmx_max_ntxqueues;
+	int vmx_max_nrxqueues;
+	uint8_t vmx_lladdr[ETHER_ADDR_LEN];
 };
 
 #define VMXNET3_STAT
 
 #ifdef VMXNET3_STAT
 struct {
-	u_int ntxdesc;
-	u_int nrxdesc;
 	u_int txhead;
 	u_int txdone;
 	u_int maxtxlen;
 	u_int rxdone;
 	u_int rxfill;
 	u_int intr;
-} vmxstat = {
-	.ntxdesc = NTXDESC,
-	.nrxdesc = NRXDESC
-};
+} vmxstat;
 #endif
 
+typedef enum {
+	VMXNET3_BARRIER_RD,
+	VMXNET3_BARRIER_WR,
+	VMXNET3_BARRIER_RDWR,
+} vmxnet3_barrier_t;
+
 #define JUMBO_LEN (MCLBYTES - ETHER_ALIGN)	/* XXX */
 #define DMAADDR(map) ((map)->dm_segs[0].ds_addr)
 
-#define READ_BAR0(sc, reg) bus_space_read_4((sc)->sc_iot0, (sc)->sc_ioh0, reg)
-#define READ_BAR1(sc, reg) bus_space_read_4((sc)->sc_iot1, (sc)->sc_ioh1, reg)
-#define WRITE_BAR0(sc, reg, val) \
-	bus_space_write_4((sc)->sc_iot0, (sc)->sc_ioh0, reg, val)
-#define WRITE_BAR1(sc, reg, val) \
-	bus_space_write_4((sc)->sc_iot1, (sc)->sc_ioh1, reg, val)
-#define WRITE_CMD(sc, cmd) WRITE_BAR1(sc, VMXNET3_BAR1_CMD, cmd)
 #define vtophys(va) 0		/* XXX ok? */
 
 int vmxnet3_match(device_t, cfdata_t, void *);
 void vmxnet3_attach(device_t, device_t, void *);
-int vmxnet3_dma_init(struct vmxnet3_softc *);
-int vmxnet3_alloc_txring(struct vmxnet3_softc *, int);
-int vmxnet3_alloc_rxring(struct vmxnet3_softc *, int);
-void vmxnet3_txinit(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
-void vmxnet3_rxinit(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
+int vmxnet3_detach(device_t, int);
+
+int vmxnet3_alloc_pci_resources(struct vmxnet3_softc *);
+void vmxnet3_free_pci_resources(struct vmxnet3_softc *);
+int vmxnet3_check_version(struct vmxnet3_softc *);
+void vmxnet3_check_multiqueue(struct vmxnet3_softc *);
+
+int vmxnet3_alloc_msix_interrupts(struct vmxnet3_softc *);
+int vmxnet3_alloc_msi_interrupts(struct vmxnet3_softc *);
+int vmxnet3_alloc_legacy_interrupts(struct vmxnet3_softc *);
+int vmxnet3_alloc_interrupts(struct vmxnet3_softc *);
+void vmxnet3_free_interrupts(struct vmxnet3_softc *);
+
+int vmxnet3_setup_msix_interrupts(struct vmxnet3_softc *);
+int vmxnet3_setup_msi_interrupt(struct vmxnet3_softc *);
+int vmxnet3_setup_legacy_interrupt(struct vmxnet3_softc *);
+void vmxnet3_set_interrupt_idx(struct vmxnet3_softc *);
+int vmxnet3_setup_interrupts(struct vmxnet3_softc *);
+
+int vmxnet3_init_rxq(struct vmxnet3_softc *, int);
+int vmxnet3_init_txq(struct vmxnet3_softc *, int);
+int vmxnet3_alloc_rxtx_queues(struct vmxnet3_softc *);
+void vmxnet3_destroy_rxq(struct vmxnet3_rxqueue *);
+void vmxnet3_destroy_txq(struct vmxnet3_txqueue *);
+void vmxnet3_free_rxtx_queues(struct vmxnet3_softc *);
+
+int vmxnet3_alloc_shared_data(struct vmxnet3_softc *);
+void vmxnet3_free_shared_data(struct vmxnet3_softc *);
+int vmxnet3_alloc_txq_data(struct vmxnet3_softc *);
+void vmxnet3_free_txq_data(struct vmxnet3_softc *);
+int vmxnet3_alloc_rxq_data(struct vmxnet3_softc *);
+void vmxnet3_free_rxq_data(struct vmxnet3_softc *);
+int vmxnet3_alloc_queue_data(struct vmxnet3_softc *);
+void vmxnet3_free_queue_data(struct vmxnet3_softc *);
+int vmxnet3_alloc_mcast_table(struct vmxnet3_softc *);
+void vmxnet3_free_mcast_table(struct vmxnet3_softc *);
+void vmxnet3_init_shared_data(struct vmxnet3_softc *);
+void vmxnet3_reinit_rss_shared_data(struct vmxnet3_softc *);
+void vmxnet3_reinit_shared_data(struct vmxnet3_softc *);
+int vmxnet3_alloc_data(struct vmxnet3_softc *);
+void vmxnet3_free_data(struct vmxnet3_softc *);
+int vmxnet3_setup_interface(struct vmxnet3_softc *);
+
+void vmxnet3_evintr(struct vmxnet3_softc *);
+void vmxnet3_txq_eof(struct vmxnet3_txqueue *);
+int vmxnet3_newbuf(struct vmxnet3_softc *, struct vmxnet3_rxring *);
+void vmxnet3_rxq_eof_discard(struct vmxnet3_rxqueue *,
+    struct vmxnet3_rxring *, int);
+void vmxnet3_rxq_discard_chain(struct vmxnet3_rxqueue *);
+void vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *, struct mbuf *);
+void vmxnet3_rxq_input(struct vmxnet3_rxqueue *,
+    struct vmxnet3_rxcompdesc *, struct mbuf *);
+void vmxnet3_rxq_eof(struct vmxnet3_rxqueue *);
+int vmxnet3_legacy_intr(void *);
+int vmxnet3_txq_intr(void *);
+int vmxnet3_rxq_intr(void *);
+int vmxnet3_event_intr(void *);
+
 void vmxnet3_txstop(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
 void vmxnet3_rxstop(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
-void vmxnet3_link_state(struct vmxnet3_softc *);
-void vmxnet3_enable_all_intrs(struct vmxnet3_softc *);
-void vmxnet3_disable_all_intrs(struct vmxnet3_softc *);
-int vmxnet3_intr(void *);
-void vmxnet3_evintr(struct vmxnet3_softc *);
-void vmxnet3_txintr(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
-void vmxnet3_rxintr(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
-void vmxnet3_iff(struct vmxnet3_softc *);
-int vmxnet3_ifflags_cb(struct ethercom *);
+void vmxnet3_stop_locked(struct vmxnet3_softc *);
+void vmxnet3_stop_rendezvous(struct vmxnet3_softc *);
+void vmxnet3_stop(struct ifnet *, int);
+
+void vmxnet3_txinit(struct vmxnet3_softc *, struct vmxnet3_txqueue *);
+int vmxnet3_rxinit(struct vmxnet3_softc *, struct vmxnet3_rxqueue *);
+int vmxnet3_reinit_queues(struct vmxnet3_softc *);
+int vmxnet3_enable_device(struct vmxnet3_softc *);
+void vmxnet3_reinit_rxfilters(struct vmxnet3_softc *);
+int vmxnet3_reinit(struct vmxnet3_softc *);
+
 void vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *, struct mbuf *);
-int vmxnet3_getbuf(struct vmxnet3_softc *, struct vmxnet3_rxring *);
-void vmxnet3_stop(struct ifnet *, int disable);
-void vmxnet3_reset(struct ifnet *);
+int vmxnet3_init_locked(struct vmxnet3_softc *);
 int vmxnet3_init(struct ifnet *);
-int vmxnet3_ioctl(struct ifnet *, u_long, void *);
-int vmxnet3_change_mtu(struct vmxnet3_softc *, int);
+
+int vmxnet3_txq_offload_ctx(struct vmxnet3_txqueue *, struct mbuf *, int *, int *);
+int vmxnet3_txq_load_mbuf(struct vmxnet3_txqueue *, struct mbuf **, bus_dmamap_t);
+void vmxnet3_txq_unload_mbuf(struct vmxnet3_txqueue *, bus_dmamap_t);
+int vmxnet3_txq_encap(struct vmxnet3_txqueue *, struct mbuf **);
+void vmxnet3_start_locked(struct ifnet *);
 void vmxnet3_start(struct ifnet *);
-int vmxnet3_load_mbuf(struct vmxnet3_softc *, struct mbuf *);
-void vmxnet3_watchdog(struct ifnet *);
+
+void vmxnet3_set_rxfilter(struct vmxnet3_softc *);
+int vmxnet3_change_mtu(struct vmxnet3_softc *, int);
+int vmxnet3_ioctl(struct ifnet *, u_long, void *);
+int vmxnet3_ifflags_cb(struct ethercom *);
+
+int vmxnet3_watchdog(struct vmxnet3_txqueue *);
+void vmxnet3_refresh_host_stats(struct vmxnet3_softc *);
+void vmxnet3_tick(void *);
+void vmxnet3_link_status(struct vmxnet3_softc *);
 void vmxnet3_media_status(struct ifnet *, struct ifmediareq *);
 int vmxnet3_media_change(struct ifnet *);
-void *vmxnet3_dma_allocmem(struct vmxnet3_softc *, u_int, u_int, bus_addr_t *);
+void vmxnet3_set_lladdr(struct vmxnet3_softc *);
+void vmxnet3_get_lladdr(struct vmxnet3_softc *);
+
+void vmxnet3_enable_all_intrs(struct vmxnet3_softc *);
+void vmxnet3_disable_all_intrs(struct vmxnet3_softc *);
+
+int vmxnet3_dma_malloc(struct vmxnet3_softc *, bus_size_t, bus_size_t,
+    struct vmxnet3_dma_alloc *);
+void vmxnet3_dma_free(struct vmxnet3_softc *, struct vmxnet3_dma_alloc *);
 
 CFATTACH_DECL3_NEW(vmx, sizeof(struct vmxnet3_softc),
-    vmxnet3_match, vmxnet3_attach, NULL, NULL, NULL, NULL, 0);
+    vmxnet3_match, vmxnet3_attach, vmxnet3_detach, NULL, NULL, NULL, 0);
+
+static inline void
+vmxnet3_write_bar0(struct vmxnet3_softc *sc, bus_size_t r, uint32_t v)
+{
+
+	bus_space_write_4(sc->vmx_iot0, sc->vmx_ioh0, r, v);
+}
+
+static inline uint32_t
+vmxnet3_read_bar1(struct vmxnet3_softc *sc, bus_size_t r)
+{
+
+	return (bus_space_read_4(sc->vmx_iot1, sc->vmx_ioh1, r));
+}
+
+static inline void
+vmxnet3_write_bar1(struct vmxnet3_softc *sc, bus_size_t r, uint32_t v)
+{
+
+	bus_space_write_4(sc->vmx_iot1, sc->vmx_ioh1, r, v);
+}
+
+static inline void
+vmxnet3_write_cmd(struct vmxnet3_softc *sc, uint32_t cmd)
+{
+
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_CMD, cmd);
+}
+
+static inline uint32_t
+vmxnet3_read_cmd(struct vmxnet3_softc *sc, uint32_t cmd)
+{
+
+	vmxnet3_write_cmd(sc, cmd);
+	return (vmxnet3_read_bar1(sc, VMXNET3_BAR1_CMD));
+}
+
+static inline void
+vmxnet3_enable_intr(struct vmxnet3_softc *sc, int irq)
+{
+	vmxnet3_write_bar0(sc, VMXNET3_BAR0_IMASK(irq), 0);
+}
+
+static inline void
+vmxnet3_disable_intr(struct vmxnet3_softc *sc, int irq)
+{
+	vmxnet3_write_bar0(sc, VMXNET3_BAR0_IMASK(irq), 1);
+}
+
+static inline void
+vmxnet3_rxr_increment_fill(struct vmxnet3_rxring *rxr)
+{
+
+	if (++rxr->vxrxr_fill == rxr->vxrxr_ndesc) {
+		rxr->vxrxr_fill = 0;
+		rxr->vxrxr_gen ^= 1;
+	}
+}
+
+static inline int
+vmxnet3_txring_avail(struct vmxnet3_txring *txr)
+{
+	int avail = txr->vxtxr_next - txr->vxtxr_head - 1;
+	return (avail < 0 ? txr->vxtxr_ndesc + avail : avail);
+}
+
+/*
+ * Since this is a purely paravirtualized device, we do not have
+ * to worry about DMA coherency. But at times, we must make sure
+ * both the compiler and CPU do not reorder memory operations.
+ */
+static inline void
+vmxnet3_barrier(struct vmxnet3_softc *sc, vmxnet3_barrier_t type)
+{
+
+	switch (type) {
+	case VMXNET3_BARRIER_RD:
+		membar_consumer();
+		break;
+	case VMXNET3_BARRIER_WR:
+		membar_producer();
+		break;
+	case VMXNET3_BARRIER_RDWR:
+		membar_sync();
+		break;
+	default:
+		panic("%s: bad barrier type %d", __func__, type);
+	}
+}
 
 int
 vmxnet3_match(device_t parent, cfdata_t match, void *aux)
@@ -193,617 +507,2297 @@ vmxnet3_attach(device_t parent, device_t
 {
 	struct vmxnet3_softc *sc = device_private(self);
 	struct pci_attach_args *pa = aux;
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	pci_intr_handle_t ih;
-	const char *intrstr;
-	void *vih;
-	u_int memtype, ver, macl, mach;
 	pcireg_t preg;
-	u_char enaddr[ETHER_ADDR_LEN];
-	char intrbuf[PCI_INTRSTR_LEN];
+	int error;
 
-	sc->sc_dev = self;
+	sc->vmx_dev = self;
+	sc->vmx_pa = pa;
+	if (pci_dma64_available(pa))
+		sc->vmx_dmat = pa->pa_dmat64;
+	else
+		sc->vmx_dmat = pa->pa_dmat;
 
 	pci_aprint_devinfo_fancy(pa, "Ethernet controller", "vmxnet3", 1);
 
-	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, 0x10);
-	if (pci_mapreg_map(pa, 0x10, memtype, 0, &sc->sc_iot0, &sc->sc_ioh0,
-	    NULL, NULL)) {
-		aprint_error_dev(sc->sc_dev, "failed to map BAR0\n");
-		return;
-	}
-	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, 0x14);
-	if (pci_mapreg_map(pa, 0x14, memtype, 0, &sc->sc_iot1, &sc->sc_ioh1,
-	    NULL, NULL)) {
-		aprint_error_dev(sc->sc_dev, "failed to map BAR1\n");
-		return;
-	}
-
-	ver = READ_BAR1(sc, VMXNET3_BAR1_VRRS);
-	if ((ver & 0x1) == 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "unsupported hardware version 0x%x\n", ver);
-		return;
-	}
-	WRITE_BAR1(sc, VMXNET3_BAR1_VRRS, 1);
-
-	ver = READ_BAR1(sc, VMXNET3_BAR1_UVRS);
-	if ((ver & 0x1) == 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "incompatiable UPT version 0x%x\n", ver);
-		return;
-	}
-	WRITE_BAR1(sc, VMXNET3_BAR1_UVRS, 1);
-
 	preg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
 	preg |= PCI_COMMAND_MASTER_ENABLE;
 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, preg);
 
-	if (pci_dma64_available(pa))
-		sc->sc_dmat = pa->pa_dmat64;
-	else
-		sc->sc_dmat = pa->pa_dmat;
-	if (vmxnet3_dma_init(sc)) {
-		aprint_error_dev(sc->sc_dev, "failed to setup DMA\n");
+	sc->vmx_mtx = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+	callout_init(&sc->vmx_tick, CALLOUT_MPSAFE);
+
+	sc->vmx_max_ntxqueues = ncpu;
+	sc->vmx_max_nrxqueues = ncpu;
+	sc->vmx_ntxdescs = 512;
+	sc->vmx_nrxdescs = 256;
+	sc->vmx_max_rxsegs = VMXNET3_MAX_RX_SEGS;
+
+	error = vmxnet3_alloc_pci_resources(sc);
+	if (error)
 		return;
-	}
 
-	if (pci_intr_map(pa, &ih)) {
-		aprint_error_dev(sc->sc_dev, "failed to map interrupt\n");
+	error = vmxnet3_check_version(sc);
+	if (error)
 		return;
-	}
-	intrstr = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf));
-	vih = pci_intr_establish(pa->pa_pc, ih, IPL_NET, vmxnet3_intr, sc);
-	if (vih == NULL) {
-		aprint_error_dev(sc->sc_dev,
-		    "unable to establish interrupt%s%s\n",
-		    intrstr ? " at " : "", intrstr ? intrstr : "");
+
+	error = vmxnet3_alloc_rxtx_queues(sc);
+	if (error)
 		return;
-	}
-	aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
 
-	WRITE_CMD(sc, VMXNET3_CMD_GET_MACL);
-	macl = READ_BAR1(sc, VMXNET3_BAR1_CMD);
-	enaddr[0] = macl;
-	enaddr[1] = macl >> 8;
-	enaddr[2] = macl >> 16;
-	enaddr[3] = macl >> 24;
-	WRITE_CMD(sc, VMXNET3_CMD_GET_MACH);
-	mach = READ_BAR1(sc, VMXNET3_BAR1_CMD);
-	enaddr[4] = mach;
-	enaddr[5] = mach >> 8;
-
-	WRITE_BAR1(sc, VMXNET3_BAR1_MACL, macl);
-	WRITE_BAR1(sc, VMXNET3_BAR1_MACH, mach);
-	aprint_normal_dev(sc->sc_dev, "Ethernet address %s\n",
-	    ether_sprintf(enaddr));
+	error = vmxnet3_alloc_interrupts(sc);
+	if (error)
+		return;
 
-	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
-	ifp->if_softc = sc;
-	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
-	ifp->if_ioctl = vmxnet3_ioctl;
-	ifp->if_start = vmxnet3_start;
-	ifp->if_watchdog = vmxnet3_watchdog;
-	ifp->if_init = vmxnet3_init;
-	ifp->if_stop = vmxnet3_stop;
-	sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
-	if (sc->sc_ds->upt_features & UPT1_F_CSUM)
-		sc->sc_ethercom.ec_if.if_capabilities |=
-		    IFCAP_CSUM_IPv4_Rx |
-		    IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
-		    IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx;
-	if (sc->sc_ds->upt_features & UPT1_F_VLAN)
-		sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_HWTAGGING;
+	vmxnet3_check_multiqueue(sc);
 
-	IFQ_SET_MAXLEN(&ifp->if_snd, NTXDESC);
-	IFQ_SET_READY(&ifp->if_snd);
+	error = vmxnet3_alloc_data(sc);
+	if (error)
+		return;
 
-	ifmedia_init(&sc->sc_media, IFM_IMASK, vmxnet3_media_change,
-	    vmxnet3_media_status);
-	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL);
-	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10G_T|IFM_FDX, 0, NULL);
-	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10G_T, 0, NULL);
-	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
-	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_1000_T, 0, NULL);
-	ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
+	error = vmxnet3_setup_interface(sc);
+	if (error)
+		return;
 
-	if_attach(ifp);
-	ether_ifattach(ifp, enaddr);
-	ether_set_ifflags_cb(&sc->sc_ethercom, vmxnet3_ifflags_cb);
-	vmxnet3_link_state(sc);
+	error = vmxnet3_setup_interrupts(sc);
+	if (error)
+		return;
+
+	sc->vmx_flags |= VMXNET3_FLAG_ATTACHED;
 }
 
 int
-vmxnet3_dma_init(struct vmxnet3_softc *sc)
+vmxnet3_detach(device_t self, int flags)
 {
-	struct vmxnet3_driver_shared *ds;
-	struct vmxnet3_txq_shared *ts;
-	struct vmxnet3_rxq_shared *rs;
-	bus_addr_t ds_pa, qs_pa, mcast_pa;
-	int i, queue, qs_len;
-	u_int major, minor, release_code, rev;
-
-	qs_len = NTXQUEUE * sizeof *ts + NRXQUEUE * sizeof *rs;
-	ts = vmxnet3_dma_allocmem(sc, qs_len, VMXNET3_DMADESC_ALIGN, &qs_pa);
-	if (ts == NULL)
-		return -1;
-	for (queue = 0; queue < NTXQUEUE; queue++)
-		sc->sc_txq[queue].ts = ts++;
-	rs = (void *)ts;
-	for (queue = 0; queue < NRXQUEUE; queue++)
-		sc->sc_rxq[queue].rs = rs++;
-
-	for (queue = 0; queue < NTXQUEUE; queue++)
-		if (vmxnet3_alloc_txring(sc, queue))
-			return -1;
-	for (queue = 0; queue < NRXQUEUE; queue++)
-		if (vmxnet3_alloc_rxring(sc, queue))
-			return -1;
+	struct vmxnet3_softc *sc;
+	struct ifnet *ifp;
 
-	sc->sc_mcast = vmxnet3_dma_allocmem(sc, 682 * ETHER_ADDR_LEN, 32, &mcast_pa);
-	if (sc->sc_mcast == NULL)
-		return -1;
+	sc = device_private(self);
+	ifp = &sc->vmx_ethercom.ec_if;
 
-	ds = vmxnet3_dma_allocmem(sc, sizeof *sc->sc_ds, 8, &ds_pa);
-	if (ds == NULL)
-		return -1;
-	sc->sc_ds = ds;
-	ds->magic = VMXNET3_REV1_MAGIC;
-	ds->version = VMXNET3_DRIVER_VERSION;
+	if (sc->vmx_flags & VMXNET3_FLAG_ATTACHED) {
+		VMXNET3_CORE_LOCK(sc);
+		vmxnet3_stop_locked(sc);
+		callout_halt(&sc->vmx_tick, sc->vmx_mtx);
+		VMXNET3_CORE_UNLOCK(sc);
 
-	/*
-	 * XXX FreeBSD version uses following values:
-	 * (Does the device behavior depend on them?)
-	 *
-	 * major = __FreeBSD_version / 100000;
-	 * minor = (__FreeBSD_version / 1000) % 100;
-	 * release_code = (__FreeBSD_version / 100) % 10;
-	 * rev = __FreeBSD_version % 100;
-	 */
-	major = 0;
-	minor = 0;
-	release_code = 0;
-	rev = 0;
-#ifdef __LP64__
-	ds->guest = release_code << 30 | rev << 22 | major << 14 | minor << 6
-	    | VMXNET3_GOS_FREEBSD | VMXNET3_GOS_64BIT;
-#else
-	ds->guest = release_code << 30 | rev << 22 | major << 14 | minor << 6
-	    | VMXNET3_GOS_FREEBSD | 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 = qs_pa;
-	ds->queue_shared_len = qs_len;
-	ds->mtu = ETHERMTU;
-	ds->ntxqueue = NTXQUEUE;
-	ds->nrxqueue = NRXQUEUE;
-	ds->mcast_table = mcast_pa;
-	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;
-	WRITE_BAR1(sc, VMXNET3_BAR1_DSL, ds_pa);
-	WRITE_BAR1(sc, VMXNET3_BAR1_DSH, (uint64_t)ds_pa >> 32);
-	return 0;
-}
+		ifmedia_delete_instance(&sc->vmx_media, IFM_INST_ANY);
 
-int
-vmxnet3_alloc_txring(struct vmxnet3_softc *sc, int queue)
-{
-	struct vmxnet3_txqueue *tq = &sc->sc_txq[queue];
-	struct vmxnet3_txq_shared *ts;
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring;
-	bus_addr_t pa, comp_pa;
-	int idx;
+		ether_ifdetach(ifp);
+		if_detach(ifp);
+	}
 
-	ring->txd = vmxnet3_dma_allocmem(sc, NTXDESC * sizeof ring->txd[0], 512, &pa);
-	if (ring->txd == NULL)
-		return -1;
-	comp_ring->txcd = vmxnet3_dma_allocmem(sc,
-	    NTXCOMPDESC * sizeof comp_ring->txcd[0], 512, &comp_pa);
-	if (comp_ring->txcd == NULL)
-		return -1;
+	vmxnet3_free_interrupts(sc);
 
-	for (idx = 0; idx < NTXDESC; idx++) {
-		if (bus_dmamap_create(sc->sc_dmat, JUMBO_LEN, NTXSEGS,
-		    JUMBO_LEN, 0, BUS_DMA_NOWAIT, &ring->dmap[idx]))
-			return -1;
-	}
-
-	ts = tq->ts;
-	memset(ts, 0, sizeof *ts);
-	ts->npending = 0;
-	ts->intr_threshold = 1;
-	ts->cmd_ring = pa;
-	ts->cmd_ring_len = NTXDESC;
-	ts->comp_ring = comp_pa;
-	ts->comp_ring_len = NTXCOMPDESC;
-	ts->driver_data = vtophys(tq);
-	ts->driver_data_len = sizeof *tq;
-	ts->intr_idx = 0;
-	ts->stopped = 1;
-	ts->error = 0;
-	return 0;
+	vmxnet3_free_data(sc);
+	vmxnet3_free_pci_resources(sc);
+	vmxnet3_free_rxtx_queues(sc);
+
+	if (sc->vmx_mtx)
+		mutex_obj_free(sc->vmx_mtx);
+
+	return (0);
 }
 
 int
-vmxnet3_alloc_rxring(struct vmxnet3_softc *sc, int queue)
+vmxnet3_alloc_pci_resources(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_rxqueue *rq = &sc->sc_rxq[queue];
-	struct vmxnet3_rxq_shared *rs;
-	struct vmxnet3_rxring *ring;
-	struct vmxnet3_comp_ring *comp_ring;
-	bus_addr_t pa[2], comp_pa;
-	int i, idx;
-
-	for (i = 0; i < 2; i++) {
-		ring = &rq->cmd_ring[i];
-		ring->rxd = vmxnet3_dma_allocmem(sc, NRXDESC * sizeof ring->rxd[0],
-		    512, &pa[i]);
-		if (ring->rxd == NULL)
-			return -1;
-	}
-	comp_ring = &rq->comp_ring;
-	comp_ring->rxcd = vmxnet3_dma_allocmem(sc,
-	    NRXCOMPDESC * sizeof comp_ring->rxcd[0], 512, &comp_pa);
-	if (comp_ring->rxcd == NULL)
-		return -1;
+	struct pci_attach_args *pa = sc->vmx_pa;
+	pcireg_t memtype;
 
-	for (i = 0; i < 2; i++) {
-		ring = &rq->cmd_ring[i];
-		ring->rid = i;
-		for (idx = 0; idx < NRXDESC; idx++) {
-			if (bus_dmamap_create(sc->sc_dmat, JUMBO_LEN, 1,
-			    JUMBO_LEN, 0, BUS_DMA_NOWAIT, &ring->dmap[idx]))
-				return -1;
-		}
+	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_BAR(0));
+	if (pci_mapreg_map(pa, PCI_BAR(0), memtype, 0, &sc->vmx_iot0, &sc->vmx_ioh0,
+	    NULL, &sc->vmx_ios0)) {
+		aprint_error_dev(sc->vmx_dev, "failed to map BAR0\n");
+		return (ENXIO);
+	}
+	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_BAR(1));
+	if (pci_mapreg_map(pa, PCI_BAR(1), memtype, 0, &sc->vmx_iot1, &sc->vmx_ioh1,
+	    NULL, &sc->vmx_ios1)) {
+		aprint_error_dev(sc->vmx_dev, "failed to map BAR1\n");
+		return (ENXIO);
+	}
+
+	if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSIX, NULL, NULL)) {
+		sc->vmx_flags |= VMXNET3_FLAG_NO_MSIX;
+		return (0);
 	}
 
-	rs = rq->rs;
-	memset(rs, 0, sizeof *rs);
-	rs->cmd_ring[0] = pa[0];
-	rs->cmd_ring[1] = pa[1];
-	rs->cmd_ring_len[0] = NRXDESC;
-	rs->cmd_ring_len[1] = NRXDESC;
-	rs->comp_ring = comp_pa;
-	rs->comp_ring_len = NRXCOMPDESC;
-	rs->driver_data = vtophys(rq);
-	rs->driver_data_len = sizeof *rq;
-	rs->intr_idx = 0;
-	rs->stopped = 1;
-	rs->error = 0;
-	return 0;
+	return (0);
 }
 
 void
-vmxnet3_txinit(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq)
+vmxnet3_free_pci_resources(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring;
-
-	ring->head = ring->next = 0;
-	ring->gen = 1;
-	comp_ring->next = 0;
-	comp_ring->gen = 1;
-	memset(ring->txd, 0, NTXDESC * sizeof ring->txd[0]);
-	memset(comp_ring->txcd, 0, NTXCOMPDESC * sizeof comp_ring->txcd[0]);
-}
 
-void
-vmxnet3_rxinit(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq)
-{
-	struct vmxnet3_rxring *ring;
-	struct vmxnet3_comp_ring *comp_ring;
-	int i, idx;
+	if (sc->vmx_ios0) {
+		bus_space_unmap(sc->vmx_iot0, sc->vmx_ioh0, sc->vmx_ios0);
+		sc->vmx_ios0 = 0;
+	}
 
-	for (i = 0; i < 2; i++) {
-		ring = &rq->cmd_ring[i];
-		ring->fill = 0;
-		ring->gen = 1;
-		memset(ring->rxd, 0, NRXDESC * sizeof ring->rxd[0]);
-		for (idx = 0; idx < NRXDESC; idx++) {
-			if (vmxnet3_getbuf(sc, ring))
-				break;
-		}
+	if (sc->vmx_ios1) {
+		bus_space_unmap(sc->vmx_iot1, sc->vmx_ioh1, sc->vmx_ios1);
+		sc->vmx_ios1 = 0;
 	}
-	comp_ring = &rq->comp_ring;
-	comp_ring->next = 0;
-	comp_ring->gen = 1;
-	memset(comp_ring->rxcd, 0, NRXCOMPDESC * sizeof comp_ring->rxcd[0]);
 }
 
-void
-vmxnet3_txstop(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq)
+int
+vmxnet3_check_version(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	int idx;
+	u_int ver;
 
-	for (idx = 0; idx < NTXDESC; idx++) {
-		if (ring->m[idx]) {
-			bus_dmamap_unload(sc->sc_dmat, ring->dmap[idx]);
-			m_freem(ring->m[idx]);
-			ring->m[idx] = NULL;
-		}
+	ver = vmxnet3_read_bar1(sc, VMXNET3_BAR1_VRRS);
+	if ((ver & 0x1) == 0) {
+		aprint_error_dev(sc->vmx_dev,
+		    "unsupported hardware version 0x%x\n", ver);
+		return (ENOTSUP);
 	}
-}
-
-void
-vmxnet3_rxstop(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq)
-{
-	struct vmxnet3_rxring *ring;
-	int i, idx;
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_VRRS, 1);
 
-	for (i = 0; i < 2; i++) {
-		ring = &rq->cmd_ring[i];
-		for (idx = 0; idx < NRXDESC; idx++) {
-			if (ring->m[idx]) {
-				m_freem(ring->m[idx]);
-				ring->m[idx] = NULL;
-			}
-		}
+	ver = vmxnet3_read_bar1(sc, VMXNET3_BAR1_UVRS);
+	if ((ver & 0x1) == 0) {
+		aprint_error_dev(sc->vmx_dev,
+		    "incompatiable UPT version 0x%x\n", ver);
+		return (ENOTSUP);
 	}
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_UVRS, 1);
+
+	return (0);
 }
 
 void
-vmxnet3_link_state(struct vmxnet3_softc *sc)
+vmxnet3_check_multiqueue(struct vmxnet3_softc *sc)
 {
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	u_int x, link, speed;
 
-	WRITE_CMD(sc, VMXNET3_CMD_GET_LINK);
-	x = READ_BAR1(sc, VMXNET3_BAR1_CMD);
-	speed = x >> 16;
-	if (x & 1) {
-		ifp->if_baudrate = IF_Mbps(speed);
-		link = LINK_STATE_UP;
-	} else
-		link = LINK_STATE_DOWN;
+	if (sc->vmx_intr_type != VMXNET3_IT_MSIX)
+		goto out;
 
-	if_link_state_change(ifp, link);
-}
+	/* Just use the maximum configured for now. */
+	sc->vmx_nrxqueues = sc->vmx_max_nrxqueues;
+	sc->vmx_ntxqueues = sc->vmx_max_ntxqueues;
 
-static inline void
-vmxnet3_enable_intr(struct vmxnet3_softc *sc, int irq)
-{
-	WRITE_BAR0(sc, VMXNET3_BAR0_IMASK(irq), 0);
+	if (sc->vmx_nrxqueues > 1)
+		sc->vmx_flags |= VMXNET3_FLAG_RSS;
+
+	return;
+
+out:
+	sc->vmx_ntxqueues = 1;
+	sc->vmx_nrxqueues = 1;
 }
 
-static inline void
-vmxnet3_disable_intr(struct vmxnet3_softc *sc, int irq)
+int
+vmxnet3_alloc_msix_interrupts(struct vmxnet3_softc *sc)
 {
-	WRITE_BAR0(sc, VMXNET3_BAR0_IMASK(irq), 1);
+	int required;
+	struct pci_attach_args *pa = sc->vmx_pa;
+
+	if (sc->vmx_flags & VMXNET3_FLAG_NO_MSIX)
+		return (1);
+
+	/* Allocate an additional vector for the events interrupt. */
+	required = sc->vmx_max_nrxqueues + sc->vmx_max_ntxqueues + 1;
+
+	if (pci_msix_count(pa->pa_pc, pa->pa_tag) < required)
+		return (1);
+
+	if (pci_msix_alloc_exact(pa, &sc->vmx_intrs, required) == 0) {
+		sc->vmx_nintrs = required;
+		return (0);
+	}
+
+	return (1);
 }
 
-void
-vmxnet3_enable_all_intrs(struct vmxnet3_softc *sc)
+int
+vmxnet3_alloc_msi_interrupts(struct vmxnet3_softc *sc)
 {
-	int i;
+	int nmsi, required;
+	struct pci_attach_args *pa = sc->vmx_pa;
 
-	sc->sc_ds->ictrl &= ~VMXNET3_ICTRL_DISABLE_ALL;
-	for (i = 0; i < VMXNET3_NINTR; i++)
-		vmxnet3_enable_intr(sc, i);
+	required = 1;
+
+	nmsi = pci_msi_count(pa->pa_pc, pa->pa_tag);
+	if (nmsi < required)
+		return (1);
+
+	if (pci_msi_alloc_exact(pa, &sc->vmx_intrs, required) == 0) {
+		sc->vmx_nintrs = required;
+		return (0);
+	}
+
+	return (1);
 }
 
-void
-vmxnet3_disable_all_intrs(struct vmxnet3_softc *sc)
+int
+vmxnet3_alloc_legacy_interrupts(struct vmxnet3_softc *sc)
 {
-	int i;
 
-	sc->sc_ds->ictrl |= VMXNET3_ICTRL_DISABLE_ALL;
-	for (i = 0; i < VMXNET3_NINTR; i++)
-		vmxnet3_disable_intr(sc, i);
+	if (pci_intx_alloc(sc->vmx_pa, &sc->vmx_intrs) == 0) {
+		sc->vmx_nintrs = 1;
+		return (0);
+	}
+
+	return (1);
 }
 
 int
-vmxnet3_intr(void *arg)
+vmxnet3_alloc_interrupts(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_softc *sc = arg;
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	u_int config;
+	int error;
+
+	config = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_INTRCFG);
+
+	sc->vmx_intr_type = config & 0x03;
+	sc->vmx_intr_mask_mode = (config >> 2) & 0x03;
+
+	switch (sc->vmx_intr_type) {
+	case VMXNET3_IT_AUTO:
+		sc->vmx_intr_type = VMXNET3_IT_MSIX;
+		/* FALLTHROUGH */
+	case VMXNET3_IT_MSIX:
+		error = vmxnet3_alloc_msix_interrupts(sc);
+		if (error == 0)
+			break;
+		sc->vmx_intr_type = VMXNET3_IT_MSI;
+		/* FALLTHROUGH */
+	case VMXNET3_IT_MSI:
+		error = vmxnet3_alloc_msi_interrupts(sc);
+		if (error == 0)
+			break;
+		sc->vmx_intr_type = VMXNET3_IT_LEGACY;
+		/* FALLTHROUGH */
+	case VMXNET3_IT_LEGACY:
+		error = vmxnet3_alloc_legacy_interrupts(sc);
+		if (error == 0)
+			break;
+		/* FALLTHROUGH */
+	default:
+		sc->vmx_intr_type = -1;
+		aprint_error_dev(sc->vmx_dev, "cannot allocate any interrupt resources\n");
+		return (ENXIO);
+	}
+
+	return (error);
+}
+
+void
+vmxnet3_free_interrupts(struct vmxnet3_softc *sc)
+{
+	pci_chipset_tag_t pc = sc->vmx_pa->pa_pc;
+	int i;
+
+	for (i = 0; i < sc->vmx_nintrs; i++) {
+		pci_intr_disestablish(pc, sc->vmx_ihs[i]);
+	}
+	pci_intr_release(pc, sc->vmx_intrs, sc->vmx_nintrs);
+}
+
+int
+vmxnet3_setup_msix_interrupts(struct vmxnet3_softc *sc)
+{
+	pci_chipset_tag_t pc = sc->vmx_pa->pa_pc;
+	struct vmxnet3_txqueue *txq;
+	struct vmxnet3_rxqueue *rxq;
+	pci_intr_handle_t *intr;
+	void **ihs;
+	int intr_idx, i;
+	const char *intrstr;
+	char intrbuf[PCI_INTRSTR_LEN];
+	char xnamebuf[32];
+
+	intr = sc->vmx_intrs;
+	intr_idx = 0;
+	ihs = sc->vmx_ihs;
+
+	for (i = 0; i < sc->vmx_ntxqueues; i++, intr++, ihs++, intr_idx++) {
+		snprintf(xnamebuf, 32, "%s: tx %d", device_xname(sc->vmx_dev), i);
+
+		txq = &sc->vmx_txq[i];
+
+		intrstr = pci_intr_string(pc, *intr, intrbuf, sizeof(intrbuf));
+
+		pci_intr_setattr(pc, intr, PCI_INTR_MPSAFE, true);
+		*ihs = pci_intr_establish_xname(pc, *intr, IPL_NET,
+		    vmxnet3_txq_intr, txq, xnamebuf);
+		if (*ihs == NULL) {
+			aprint_error_dev(sc->vmx_dev,
+			    "unable to establish tx interrupt at %s\n", intrstr);
+			return (-1);
+		}
+		aprint_normal_dev(sc->vmx_dev, "tx interrupting at %s\n", intrstr);
+
+		txq->vxtxq_intr_idx = intr_idx;
+	}
+
+	for (i = 0; i < sc->vmx_nrxqueues; i++, intr++, ihs++, intr_idx++) {
+		snprintf(xnamebuf, 32, "%s: rx %d", device_xname(sc->vmx_dev), i);
+
+		rxq = &sc->vmx_rxq[i];
+
+		intrstr = pci_intr_string(pc, *intr, intrbuf, sizeof(intrbuf));
+
+		pci_intr_setattr(pc, intr, PCI_INTR_MPSAFE, true);
+		*ihs = pci_intr_establish_xname(pc, *intr, IPL_NET,
+		    vmxnet3_rxq_intr, rxq, xnamebuf);
+		if (*ihs == NULL) {
+			aprint_error_dev(sc->vmx_dev,
+			    "unable to establish rx interrupt at %s\n", intrstr);
+			return (-1);
+		}
+		aprint_normal_dev(sc->vmx_dev, "rx interrupting at %s\n", intrstr);
+
+		rxq->vxrxq_intr_idx = intr_idx;
+	}
+
+	intrstr = pci_intr_string(pc, *intr, intrbuf, sizeof(intrbuf));
+
+	snprintf(xnamebuf, 32, "%s: link", device_xname(sc->vmx_dev));
+	pci_intr_setattr(pc, intr, PCI_INTR_MPSAFE, true);
+	*ihs = pci_intr_establish_xname(pc, *intr, IPL_NET,
+	    vmxnet3_event_intr, sc, xnamebuf);
+	if (*ihs == NULL) {
+		aprint_error_dev(sc->vmx_dev,
+		    "unable to establish event interrupt at %s\n", intrstr);
+		return (-1);
+	}
+	aprint_normal_dev(sc->vmx_dev, "event interrupting at %s\n", intrstr);
+
+	sc->vmx_event_intr_idx = intr_idx;
+
+	return (0);
+}
+
+int
+vmxnet3_setup_msi_interrupt(struct vmxnet3_softc *sc)
+{
+	pci_chipset_tag_t pc = sc->vmx_pa->pa_pc;
+	pci_intr_handle_t *intr;
+	void **ihs;
+	int i;
+	const char *intrstr;
+	char intrbuf[PCI_INTRSTR_LEN];
+	char xnamebuf[32];
+
+	intr = &sc->vmx_intrs[0];
+	ihs = sc->vmx_ihs;
+
+	intrstr = pci_intr_string(pc, *intr, intrbuf, sizeof(intrbuf));
+
+	snprintf(xnamebuf, 32, "%s: msi", device_xname(sc->vmx_dev));
+	pci_intr_setattr(pc, intr, PCI_INTR_MPSAFE, true);
+	*ihs = pci_intr_establish_xname(pc, *intr, IPL_NET,
+	    vmxnet3_legacy_intr, sc, xnamebuf);
+	if (*ihs == NULL) {
+		aprint_error_dev(sc->vmx_dev,
+		    "unable to establish interrupt at %s\n", intrstr);
+		return (-1);
+	}
+	aprint_normal_dev(sc->vmx_dev, "interrupting at %s\n", intrstr);
+
+	for (i = 0; i < sc->vmx_ntxqueues; i++)
+		sc->vmx_txq[i].vxtxq_intr_idx = 0;
+	for (i = 0; i < sc->vmx_nrxqueues; i++)
+		sc->vmx_rxq[i].vxrxq_intr_idx = 0;
+	sc->vmx_event_intr_idx = 0;
+
+	return (0);
+}
+
+int
+vmxnet3_setup_legacy_interrupt(struct vmxnet3_softc *sc)
+{
+	pci_chipset_tag_t pc = sc->vmx_pa->pa_pc;
+	pci_intr_handle_t *intr;
+	void **ihs;
+	int i;
+	const char *intrstr;
+	char intrbuf[PCI_INTRSTR_LEN];
+	char xnamebuf[32];
+
+	intr = &sc->vmx_intrs[0];
+	ihs = sc->vmx_ihs;
+
+	intrstr = pci_intr_string(pc, *intr, intrbuf, sizeof(intrbuf));
+
+	snprintf(xnamebuf, 32, "%s:legacy", device_xname(sc->vmx_dev));
+	pci_intr_setattr(pc, intr, PCI_INTR_MPSAFE, true);
+	*ihs = pci_intr_establish_xname(pc, *intr, IPL_NET,
+	    vmxnet3_legacy_intr, sc, xnamebuf);
+	if (*ihs == NULL) {
+		aprint_error_dev(sc->vmx_dev,
+		    "unable to establish interrupt at %s\n", intrstr);
+		return (-1);
+	}
+	aprint_normal_dev(sc->vmx_dev, "interrupting at %s\n", intrstr);
+
+	for (i = 0; i < sc->vmx_ntxqueues; i++)
+		sc->vmx_txq[i].vxtxq_intr_idx = 0;
+	for (i = 0; i < sc->vmx_nrxqueues; i++)
+		sc->vmx_rxq[i].vxrxq_intr_idx = 0;
+	sc->vmx_event_intr_idx = 0;
+
+	return (0);
+}
+
+void
+vmxnet3_set_interrupt_idx(struct vmxnet3_softc *sc)
+{
+	struct vmxnet3_txqueue *txq;
+	struct vmxnet3_txq_shared *txs;
+	struct vmxnet3_rxqueue *rxq;
+	struct vmxnet3_rxq_shared *rxs;
+	int i;
+
+	sc->vmx_ds->evintr = sc->vmx_event_intr_idx;
+
+	for (i = 0; i < sc->vmx_ntxqueues; i++) {
+		txq = &sc->vmx_txq[i];
+		txs = txq->vxtxq_ts;
+		txs->intr_idx = txq->vxtxq_intr_idx;
+	}
+
+	for (i = 0; i < sc->vmx_nrxqueues; i++) {
+		rxq = &sc->vmx_rxq[i];
+		rxs = rxq->vxrxq_rs;
+		rxs->intr_idx = rxq->vxrxq_intr_idx;
+	}
+}
+
+int
+vmxnet3_setup_interrupts(struct vmxnet3_softc *sc)
+{
+	int error;
+
+	switch (sc->vmx_intr_type) {
+	case VMXNET3_IT_MSIX:
+		error = vmxnet3_setup_msix_interrupts(sc);
+		break;
+	case VMXNET3_IT_MSI:
+		error = vmxnet3_setup_msi_interrupt(sc);
+		break;
+	case VMXNET3_IT_LEGACY:
+		error = vmxnet3_setup_legacy_interrupt(sc);
+		break;
+	default:
+		panic("%s: invalid interrupt type %d", __func__,
+		    sc->vmx_intr_type);
+	}
+
+	if (error == 0)
+		vmxnet3_set_interrupt_idx(sc);
+
+	return (error);
+}
+
+int
+vmxnet3_init_rxq(struct vmxnet3_softc *sc, int q)
+{
+	struct vmxnet3_rxqueue *rxq;
+	struct vmxnet3_rxring *rxr;
+	int i;
+
+	rxq = &sc->vmx_rxq[q];
+
+	snprintf(rxq->vxrxq_name, sizeof(rxq->vxrxq_name), "%s-rx%d",
+	    device_xname(sc->vmx_dev), q);
+	rxq->vxrxq_mtx = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET /* XXX */);
+
+	rxq->vxrxq_sc = sc;
+	rxq->vxrxq_id = q;
+
+	for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+		rxr = &rxq->vxrxq_cmd_ring[i];
+		rxr->vxrxr_rid = i;
+		rxr->vxrxr_ndesc = sc->vmx_nrxdescs;
+		rxr->vxrxr_rxbuf = kmem_zalloc(rxr->vxrxr_ndesc *
+		    sizeof(struct vmxnet3_rxbuf), KM_SLEEP);
+		if (rxr->vxrxr_rxbuf == NULL)
+			return (ENOMEM);
+
+		rxq->vxrxq_comp_ring.vxcr_ndesc += sc->vmx_nrxdescs;
+	}
+
+	return (0);
+}
+
+int
+vmxnet3_init_txq(struct vmxnet3_softc *sc, int q)
+{
+	struct vmxnet3_txqueue *txq;
+	struct vmxnet3_txring *txr;
+
+	txq = &sc->vmx_txq[q];
+	txr = &txq->vxtxq_cmd_ring;
+
+	snprintf(txq->vxtxq_name, sizeof(txq->vxtxq_name), "%s-tx%d",
+	    device_xname(sc->vmx_dev), q);
+	txq->vxtxq_mtx = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET /* XXX */);
+
+	txq->vxtxq_sc = sc;
+	txq->vxtxq_id = q;
+
+	txr->vxtxr_ndesc = sc->vmx_ntxdescs;
+	txr->vxtxr_txbuf = kmem_zalloc(txr->vxtxr_ndesc *
+	    sizeof(struct vmxnet3_txbuf), KM_SLEEP);
+	if (txr->vxtxr_txbuf == NULL)
+		return (ENOMEM);
+
+	txq->vxtxq_comp_ring.vxcr_ndesc = sc->vmx_ntxdescs;
+
+	return (0);
+}
+
+int
+vmxnet3_alloc_rxtx_queues(struct vmxnet3_softc *sc)
+{
+	int i, error;
+
+	KASSERT(!cpu_intr_p());
+	KASSERT(!cpu_softintr_p());
+
+	/*
+	 * Only attempt to create multiple queues if MSIX is available.
+	 * This check prevents us from allocating queue structures that
+	 * we will not use.
+	 *
+	 * FreeBSD:
+	 * MSIX is disabled by default because its apparently broken for
+	 * devices passed through by at least ESXi 5.1.
+	 * The hw.pci.honor_msi_blacklist tunable must be set to zero for MSIX.
+	 */
+	if (sc->vmx_flags & VMXNET3_FLAG_NO_MSIX) {
+		sc->vmx_max_nrxqueues = 1;
+		sc->vmx_max_ntxqueues = 1;
+	}
+
+	sc->vmx_rxq = kmem_zalloc(
+	    sizeof(struct vmxnet3_rxqueue) * sc->vmx_max_nrxqueues, KM_SLEEP);
+	sc->vmx_txq = kmem_zalloc(
+	    sizeof(struct vmxnet3_txqueue) * sc->vmx_max_ntxqueues, KM_SLEEP);
+	if (sc->vmx_rxq == NULL || sc->vmx_txq == NULL)
+		return (ENOMEM);
+
+	for (i = 0; i < sc->vmx_max_nrxqueues; i++) {
+		error = vmxnet3_init_rxq(sc, i);
+		if (error)
+			return (error);
+	}
+
+	for (i = 0; i < sc->vmx_max_ntxqueues; i++) {
+		error = vmxnet3_init_txq(sc, i);
+		if (error)
+			return (error);
+	}
+
+	return (0);
+}
+
+void
+vmxnet3_destroy_rxq(struct vmxnet3_rxqueue *rxq)
+{
+	struct vmxnet3_rxring *rxr;
+	int i;
+
+	rxq->vxrxq_sc = NULL;
+	rxq->vxrxq_id = -1;
+
+	for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+		rxr = &rxq->vxrxq_cmd_ring[i];
+
+		if (rxr->vxrxr_rxbuf != NULL) {
+			kmem_free(rxr->vxrxr_rxbuf,
+			    rxr->vxrxr_ndesc * sizeof(struct vmxnet3_rxbuf));
+			rxr->vxrxr_rxbuf = NULL;
+		}
+	}
+
+	if (rxq->vxrxq_mtx != NULL)
+		mutex_obj_free(rxq->vxrxq_mtx);
+}
+
+void
+vmxnet3_destroy_txq(struct vmxnet3_txqueue *txq)
+{
+	struct vmxnet3_txring *txr;
+
+	txr = &txq->vxtxq_cmd_ring;
+
+	txq->vxtxq_sc = NULL;
+	txq->vxtxq_id = -1;
+
+	if (txr->vxtxr_txbuf != NULL) {
+		kmem_free(txr->vxtxr_txbuf,
+		    txr->vxtxr_ndesc * sizeof(struct vmxnet3_txbuf));
+		txr->vxtxr_txbuf = NULL;
+	}
+
+	if (txq->vxtxq_mtx != NULL)
+		mutex_obj_free(txq->vxtxq_mtx);
+}
+
+void
+vmxnet3_free_rxtx_queues(struct vmxnet3_softc *sc)
+{
+	int i;
+
+	if (sc->vmx_rxq != NULL) {
+		for (i = 0; i < sc->vmx_max_nrxqueues; i++)
+			vmxnet3_destroy_rxq(&sc->vmx_rxq[i]);
+		kmem_free(sc->vmx_rxq,
+		    sizeof(struct vmxnet3_rxqueue) * sc->vmx_max_nrxqueues);
+		sc->vmx_rxq = NULL;
+	}
+
+	if (sc->vmx_txq != NULL) {
+		for (i = 0; i < sc->vmx_max_ntxqueues; i++)
+			vmxnet3_destroy_txq(&sc->vmx_txq[i]);
+		kmem_free(sc->vmx_txq,
+		    sizeof(struct vmxnet3_txqueue) * sc->vmx_max_ntxqueues);
+		sc->vmx_txq = NULL;
+	}
+}
+
+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 = sc->vmx_ntxqueues * sizeof(struct vmxnet3_txq_shared) +
+	    sc->vmx_nrxqueues * 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 < sc->vmx_ntxqueues; i++) {
+		sc->vmx_txq[i].vxtxq_ts = (struct vmxnet3_txq_shared *) kva;
+		kva += sizeof(struct vmxnet3_txq_shared);
+	}
+	for (i = 0; i < sc->vmx_nrxqueues; i++) {
+		sc->vmx_rxq[i].vxrxq_rs = (struct vmxnet3_rxq_shared *) kva;
+		kva += sizeof(struct vmxnet3_rxq_shared);
+	}
+
+	if (sc->vmx_flags & VMXNET3_FLAG_RSS) {
+		size = sizeof(struct vmxnet3_rss_shared);
+		error = vmxnet3_dma_malloc(sc, size, 128, &sc->vmx_rss_dma);
+		if (error) {
+			device_printf(dev, "cannot alloc rss shared memory\n");
+			return (error);
+		}
+		sc->vmx_rss =
+		    (struct vmxnet3_rss_shared *) sc->vmx_rss_dma.dma_vaddr;
+	}
+
+	return (0);
+}
+
+void
+vmxnet3_free_shared_data(struct vmxnet3_softc *sc)
+{
+
+	if (sc->vmx_rss != NULL) {
+		vmxnet3_dma_free(sc, &sc->vmx_rss_dma);
+		sc->vmx_rss = NULL;
+	}
+
+	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;
+	}
+}
+
+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;
+
+	for (q = 0; q < sc->vmx_ntxqueues; q++) {
+		txq = &sc->vmx_txq[q];
+		txr = &txq->vxtxq_cmd_ring;
+		txc = &txq->vxtxq_comp_ring;
+
+		descsz = txr->vxtxr_ndesc * sizeof(struct vmxnet3_txdesc);
+		compsz = txr->vxtxr_ndesc * sizeof(struct vmxnet3_txcompdesc);
+
+		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 < txr->vxtxr_ndesc; i++) {
+			error = bus_dmamap_create(sc->vmx_dmat, VMXNET3_TX_MAXSIZE,
+			    VMXNET3_TX_MAXSEGS, VMXNET3_TX_MAXSEGSIZE, 0, BUS_DMA_NOWAIT,
+			    &txr->vxtxr_txbuf[i].vtxb_dmamap);
+			if (error) {
+				device_printf(dev, "unable to create Tx buf "
+				    "dmamap for queue %d idx %d\n", q, i);
+				return (error);
+			}
+		}
+	}
+
+	return (0);
+}
+
+void
+vmxnet3_free_txq_data(struct vmxnet3_softc *sc)
+{
+	struct vmxnet3_txqueue *txq;
+	struct vmxnet3_txring *txr;
+	struct vmxnet3_comp_ring *txc;
+	struct vmxnet3_txbuf *txb;
+	int i, q;
+
+	for (q = 0; q < sc->vmx_ntxqueues; q++) {
+		txq = &sc->vmx_txq[q];
+		txr = &txq->vxtxq_cmd_ring;
+		txc = &txq->vxtxq_comp_ring;
+
+		for (i = 0; i < txr->vxtxr_ndesc; i++) {
+			txb = &txr->vxtxr_txbuf[i];
+			if (txb->vtxb_dmamap != NULL) {
+				bus_dmamap_destroy(sc->vmx_dmat,
+				    txb->vtxb_dmamap);
+				txb->vtxb_dmamap = 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;
+		}
+	}
+}
+
+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;
+
+	for (q = 0; q < sc->vmx_nrxqueues; q++) {
+		rxq = &sc->vmx_rxq[q];
+		rxc = &rxq->vxrxq_comp_ring;
+		compsz = 0;
+
+		for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+			rxr = &rxq->vxrxq_cmd_ring[i];
+
+			descsz = rxr->vxrxr_ndesc *
+			    sizeof(struct vmxnet3_rxdesc);
+			compsz += rxr->vxrxr_ndesc *
+			    sizeof(struct vmxnet3_rxcompdesc);
+
+			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 < VMXNET3_RXRINGS_PERQ; i++) {
+			rxr = &rxq->vxrxq_cmd_ring[i];
+
+			error = bus_dmamap_create(sc->vmx_dmat, JUMBO_LEN, 1,
+			    JUMBO_LEN, 0, BUS_DMA_NOWAIT,
+			    &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 < rxr->vxrxr_ndesc; j++) {
+				error = bus_dmamap_create(sc->vmx_dmat, JUMBO_LEN, 1,
+				    JUMBO_LEN, 0, BUS_DMA_NOWAIT,
+				    &rxr->vxrxr_rxbuf[j].vrxb_dmamap);
+				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);
+}
+
+void
+vmxnet3_free_rxq_data(struct vmxnet3_softc *sc)
+{
+	struct vmxnet3_rxqueue *rxq;
+	struct vmxnet3_rxring *rxr;
+	struct vmxnet3_comp_ring *rxc;
+	struct vmxnet3_rxbuf *rxb;
+	int i, j, q;
+
+	for (q = 0; q < sc->vmx_nrxqueues; q++) {
+		rxq = &sc->vmx_rxq[q];
+		rxc = &rxq->vxrxq_comp_ring;
+
+		for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+			rxr = &rxq->vxrxq_cmd_ring[i];
+
+			if (rxr->vxrxr_spare_dmap != NULL) {
+				bus_dmamap_destroy(sc->vmx_dmat,
+				    rxr->vxrxr_spare_dmap);
+				rxr->vxrxr_spare_dmap = NULL;
+			}
+
+			for (j = 0; j < rxr->vxrxr_ndesc; j++) {
+				rxb = &rxr->vxrxr_rxbuf[j];
+				if (rxb->vrxb_dmamap != NULL) {
+					bus_dmamap_destroy(sc->vmx_dmat,
+					    rxb->vrxb_dmamap);
+					rxb->vrxb_dmamap = NULL;
+				}
+			}
+		}
+
+		if (rxc->vxcr_u.rxcd != NULL) {
+			vmxnet3_dma_free(sc, &rxc->vxcr_dma);
+			rxc->vxcr_u.rxcd = NULL;
+		}
+
+		for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+			rxr = &rxq->vxrxq_cmd_ring[i];
+
+			if (rxr->vxrxr_rxd != NULL) {
+				vmxnet3_dma_free(sc, &rxr->vxrxr_dma);
+				rxr->vxrxr_rxd = NULL;
+			}
+		}
+	}
+}
+
+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);
+}
+
+void
+vmxnet3_free_queue_data(struct vmxnet3_softc *sc)
+{
+
+	if (sc->vmx_rxq != NULL)
+		vmxnet3_free_rxq_data(sc);
+
+	if (sc->vmx_txq != NULL)
+		vmxnet3_free_txq_data(sc);
+}
+
+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);
+}
+
+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;
+	}
+}
+
+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;
+	int i;
+
+	ds = sc->vmx_ds;
+
+	/*
+	 * Initialize fields of the shared data that remains the same across
+	 * reinits. Note the shared data is zero'd when allocated.
+	 */
+
+	ds->magic = VMXNET3_REV1_MAGIC;
+
+	/* DriverInfo */
+	ds->version = VMXNET3_DRIVER_VERSION;
+	ds->guest = VMXNET3_GOS_FREEBSD |
+#ifdef __LP64__
+	    VMXNET3_GOS_64BIT;
+#else
+	    VMXNET3_GOS_32BIT;
+#endif
+	ds->vmxnet3_revision = 1;
+	ds->upt_version = 1;
+
+	/* Misc. conf */
+	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->nrxsg_max = sc->vmx_max_rxsegs;
+
+	/* RSS conf */
+	if (sc->vmx_flags & VMXNET3_FLAG_RSS) {
+		ds->rss.version = 1;
+		ds->rss.paddr = sc->vmx_rss_dma.dma_paddr;
+		ds->rss.len = sc->vmx_rss_dma.dma_size;
+	}
+
+	/* Interrupt control. */
+	ds->automask = sc->vmx_intr_mask_mode == VMXNET3_IMM_AUTO;
+	ds->nintr = sc->vmx_nintrs;
+	ds->evintr = sc->vmx_event_intr_idx;
+	ds->ictrl = VMXNET3_ICTRL_DISABLE_ALL;
+
+	for (i = 0; i < sc->vmx_nintrs; i++)
+		ds->modlevel[i] = UPT1_IMOD_ADAPTIVE;
+
+	/* Receive filter. */
+	ds->mcast_table = sc->vmx_mcast_dma.dma_paddr;
+	ds->mcast_tablelen = sc->vmx_mcast_dma.dma_size;
+
+	/* Tx queues */
+	for (i = 0; i < sc->vmx_ntxqueues; i++) {
+		txq = &sc->vmx_txq[i];
+		txs = txq->vxtxq_ts;
+
+		txs->cmd_ring = txq->vxtxq_cmd_ring.vxtxr_dma.dma_paddr;
+		txs->cmd_ring_len = txq->vxtxq_cmd_ring.vxtxr_ndesc;
+		txs->comp_ring = txq->vxtxq_comp_ring.vxcr_dma.dma_paddr;
+		txs->comp_ring_len = txq->vxtxq_comp_ring.vxcr_ndesc;
+		txs->driver_data = vtophys(txq);
+		txs->driver_data_len = sizeof(struct vmxnet3_txqueue);
+	}
+
+	/* Rx queues */
+	for (i = 0; i < sc->vmx_nrxqueues; 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] = rxq->vxrxq_cmd_ring[0].vxrxr_ndesc;
+		rxs->cmd_ring[1] = rxq->vxrxq_cmd_ring[1].vxrxr_dma.dma_paddr;
+		rxs->cmd_ring_len[1] = rxq->vxrxq_cmd_ring[1].vxrxr_ndesc;
+		rxs->comp_ring = rxq->vxrxq_comp_ring.vxcr_dma.dma_paddr;
+		rxs->comp_ring_len = rxq->vxrxq_comp_ring.vxcr_ndesc;
+		rxs->driver_data = vtophys(rxq);
+		rxs->driver_data_len = sizeof(struct vmxnet3_rxqueue);
+	}
+}
+
+void
+vmxnet3_reinit_rss_shared_data(struct vmxnet3_softc *sc)
+{
+	/*
+	 * Use the same key as the Linux driver until FreeBSD can do
+	 * RSS (presumably Toeplitz) in software.
+	 */
+	static const uint8_t rss_key[UPT1_RSS_MAX_KEY_SIZE] = {
+	    0x3b, 0x56, 0xd1, 0x56, 0x13, 0x4a, 0xe7, 0xac,
+	    0xe8, 0x79, 0x09, 0x75, 0xe8, 0x65, 0x79, 0x28,
+	    0x35, 0x12, 0xb9, 0x56, 0x7c, 0x76, 0x4b, 0x70,
+	    0xd8, 0x56, 0xa3, 0x18, 0x9b, 0x0a, 0xee, 0xf3,
+	    0x96, 0xa6, 0x9f, 0x8f, 0x9e, 0x8c, 0x90, 0xc9,
+	};
+
+	struct vmxnet3_rss_shared *rss;
+	int i;
+
+	rss = sc->vmx_rss;
+
+	rss->hash_type =
+	    UPT1_RSS_HASH_TYPE_IPV4 | UPT1_RSS_HASH_TYPE_TCP_IPV4 |
+	    UPT1_RSS_HASH_TYPE_IPV6 | UPT1_RSS_HASH_TYPE_TCP_IPV6;
+	rss->hash_func = UPT1_RSS_HASH_FUNC_TOEPLITZ;
+	rss->hash_key_size = UPT1_RSS_MAX_KEY_SIZE;
+	rss->ind_table_size = UPT1_RSS_MAX_IND_TABLE_SIZE;
+	memcpy(rss->hash_key, rss_key, UPT1_RSS_MAX_KEY_SIZE);
+
+	for (i = 0; i < UPT1_RSS_MAX_IND_TABLE_SIZE; i++)
+		rss->ind_table[i] = i % sc->vmx_nrxqueues;
+}
+
+void
+vmxnet3_reinit_shared_data(struct vmxnet3_softc *sc)
+{
+	struct ifnet *ifp;
+	struct vmxnet3_driver_shared *ds;
+
+	ifp = &sc->vmx_ethercom.ec_if;
+	ds = sc->vmx_ds;
+
+	ds->mtu = ifp->if_mtu;
+	ds->ntxqueue = sc->vmx_ntxqueues;
+	ds->nrxqueue = sc->vmx_nrxqueues;
+
+	ds->upt_features = 0;
+	if (ifp->if_capenable &
+	    (IFCAP_CSUM_IPv4_Rx | IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx |
+	    IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx))
+		ds->upt_features |= UPT1_F_CSUM;
+	if (sc->vmx_ethercom.ec_capenable & ETHERCAP_VLAN_HWTAGGING)
+		ds->upt_features |= UPT1_F_VLAN;
+
+	if (sc->vmx_flags & VMXNET3_FLAG_RSS) {
+		ds->upt_features |= UPT1_F_RSS;
+		vmxnet3_reinit_rss_shared_data(sc);
+	}
+
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSL, sc->vmx_ds_dma.dma_paddr);
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_DSH,
+	    (uint64_t) sc->vmx_ds_dma.dma_paddr >> 32);
+}
+
+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);
+}
+
+void
+vmxnet3_free_data(struct vmxnet3_softc *sc)
+{
+
+	vmxnet3_free_mcast_table(sc);
+	vmxnet3_free_queue_data(sc);
+	vmxnet3_free_shared_data(sc);
+}
+
+int
+vmxnet3_setup_interface(struct vmxnet3_softc *sc)
+{
+	struct ifnet *ifp = &sc->vmx_ethercom.ec_if;
+
+	vmxnet3_get_lladdr(sc);
+	aprint_normal_dev(sc->vmx_dev, "Ethernet address %s\n",
+	    ether_sprintf(sc->vmx_lladdr));
+	vmxnet3_set_lladdr(sc);
+
+	strlcpy(ifp->if_xname, device_xname(sc->vmx_dev), IFNAMSIZ);
+	ifp->if_softc = sc;
+	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
+	ifp->if_ioctl = vmxnet3_ioctl;
+	ifp->if_start = vmxnet3_start;
+	ifp->if_watchdog = NULL;
+	ifp->if_init = vmxnet3_init;
+	ifp->if_stop = vmxnet3_stop;
+	sc->vmx_ethercom.ec_if.if_capabilities |=IFCAP_CSUM_IPv4_Rx |
+		    IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
+		    IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
+		    IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_TCPv6_Rx |
+		    IFCAP_CSUM_UDPv6_Tx | IFCAP_CSUM_UDPv6_Rx;
+
+	ifp->if_capenable = ifp->if_capabilities;
+
+	sc->vmx_ethercom.ec_if.if_capabilities |= IFCAP_TSOv4 | IFCAP_TSOv6;
+
+	sc->vmx_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING;
+
+	IFQ_SET_MAXLEN(&ifp->if_snd, sc->vmx_ntxdescs);
+	IFQ_SET_READY(&ifp->if_snd);
+
+	ifmedia_init(&sc->vmx_media, IFM_IMASK, vmxnet3_media_change,
+	    vmxnet3_media_status);
+	ifmedia_add(&sc->vmx_media, IFM_ETHER|IFM_AUTO, 0, NULL);
+	ifmedia_add(&sc->vmx_media, IFM_ETHER|IFM_10G_T|IFM_FDX, 0, NULL);
+	ifmedia_add(&sc->vmx_media, IFM_ETHER|IFM_10G_T, 0, NULL);
+	ifmedia_add(&sc->vmx_media, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
+	ifmedia_add(&sc->vmx_media, IFM_ETHER|IFM_1000_T, 0, NULL);
+	ifmedia_set(&sc->vmx_media, IFM_ETHER|IFM_AUTO);
+
+	if_attach(ifp);
+	ether_ifattach(ifp, sc->vmx_lladdr);
+	ether_set_ifflags_cb(&sc->vmx_ethercom, vmxnet3_ifflags_cb);
+	vmxnet3_link_status(sc);
+
+	return (0);
+}
+
+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_ethercom.ec_if;
+	reset = 0;
+
+	VMXNET3_CORE_LOCK(sc);
+
+	/* Clear events. */
+	event = sc->vmx_ds->event;
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_EVENT, event);
+
+	if (event & VMXNET3_EVENT_LINK) {
+		vmxnet3_link_status(sc);
+		if (sc->vmx_link_active != 0)
+			vmxnet3_start(&sc->vmx_ethercom.ec_if);
+	}
+
+	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);
+		device_printf(dev, "Rx/Tx queue error event ... resetting\n");
+	}
+
+	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_flags &= ~IFF_RUNNING;
+		vmxnet3_init_locked(sc);
+	}
+
+	VMXNET3_CORE_UNLOCK(sc);
+}
+
+void
+vmxnet3_txq_eof(struct vmxnet3_txqueue *txq)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_txring *txr;
+	struct vmxnet3_comp_ring *txc;
+	struct vmxnet3_txcompdesc *txcd;
+	struct vmxnet3_txbuf *txb;
+	struct mbuf *m;
+	u_int sop;
+
+	sc = txq->vxtxq_sc;
+	txr = &txq->vxtxq_cmd_ring;
+	txc = &txq->vxtxq_comp_ring;
+
+	VMXNET3_TXQ_LOCK_ASSERT(txq);
+
+	for (;;) {
+		txcd = &txc->vxcr_u.txcd[txc->vxcr_next];
+		if (txcd->gen != txc->vxcr_gen)
+			break;
+		vmxnet3_barrier(sc, VMXNET3_BARRIER_RD);
+
+		if (++txc->vxcr_next == txc->vxcr_ndesc) {
+			txc->vxcr_next = 0;
+			txc->vxcr_gen ^= 1;
+		}
+
+		sop = txr->vxtxr_next;
+		txb = &txr->vxtxr_txbuf[sop];
+
+		if ((m = txb->vtxb_m) != NULL) {
+			bus_dmamap_sync(sc->vmx_dmat, txb->vtxb_dmamap,
+			    0, txb->vtxb_dmamap->dm_mapsize,
+			    BUS_DMASYNC_POSTWRITE);
+			bus_dmamap_unload(sc->vmx_dmat, txb->vtxb_dmamap);
+
+			txq->vxtxq_stats.vmtxs_opackets++;
+			txq->vxtxq_stats.vmtxs_obytes += m->m_pkthdr.len;
+			if (m->m_flags & M_MCAST)
+				txq->vxtxq_stats.vmtxs_omcasts++;
+
+			m_freem(m);
+			txb->vtxb_m = NULL;
+		}
+
+		txr->vxtxr_next = (txcd->eop_idx + 1) % txr->vxtxr_ndesc;
+	}
+
+	if (txr->vxtxr_head == txr->vxtxr_next)
+		txq->vxtxq_watchdog = 0;
+}
+
+int
+vmxnet3_newbuf(struct vmxnet3_softc *sc, struct vmxnet3_rxring *rxr)
+{
+	struct mbuf *m;
+	struct vmxnet3_rxdesc *rxd;
+	struct vmxnet3_rxbuf *rxb;
+	bus_dma_tag_t tag;
+	bus_dmamap_t dmap;
+	int idx, btype, error;
+
+	tag = sc->vmx_dmat;
+	dmap = rxr->vxrxr_spare_dmap;
+	idx = rxr->vxrxr_fill;
+	rxd = &rxr->vxrxr_rxd[idx];
+	rxb = &rxr->vxrxr_rxbuf[idx];
+
+	/* Don't allocate buffers for ring 2 for now. */
+	if (rxr->vxrxr_rid != 0)
+		return -1;
+	btype = VMXNET3_BTYPE_HEAD;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL)
+		return (ENOBUFS);
+
+	MCLGET(m, M_DONTWAIT);
+	if ((m->m_flags & M_EXT) == 0) {
+		sc->vmx_stats.vmst_mgetcl_failed++;
+		m_freem(m);
+		return (ENOBUFS);
+	}
+
+	m->m_pkthdr.len = m->m_len = JUMBO_LEN;
+	m_adj(m, ETHER_ALIGN);
+
+	error = bus_dmamap_load_mbuf(sc->vmx_dmat, dmap, m, BUS_DMA_NOWAIT);
+	if (error) {
+		m_freem(m);
+		sc->vmx_stats.vmst_mbuf_load_failed++;
+		return (error);
+	}
+
+	if (rxb->vrxb_m != NULL) {
+		bus_dmamap_sync(tag, rxb->vrxb_dmamap,
+		    0, rxb->vrxb_dmamap->dm_mapsize,
+		    BUS_DMASYNC_POSTREAD);
+		bus_dmamap_unload(tag, rxb->vrxb_dmamap);
+	}
+
+	rxr->vxrxr_spare_dmap = rxb->vrxb_dmamap;
+	rxb->vrxb_dmamap = dmap;
+	rxb->vrxb_m = m;
+
+	rxd->addr = DMAADDR(dmap);
+	rxd->len = m->m_pkthdr.len;
+	rxd->btype = btype;
+	rxd->gen = rxr->vxrxr_gen;
+
+	vmxnet3_rxr_increment_fill(rxr);
+	return (0);
+}
+
+
+
+void
+vmxnet3_rxq_eof_discard(struct vmxnet3_rxqueue *rxq,
+    struct vmxnet3_rxring *rxr, int idx)
+{
+	struct vmxnet3_rxdesc *rxd;
+
+	rxd = &rxr->vxrxr_rxd[idx];
+	rxd->gen = rxr->vxrxr_gen;
+	vmxnet3_rxr_increment_fill(rxr);
+}
+
+void
+vmxnet3_rxq_discard_chain(struct vmxnet3_rxqueue *rxq)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_rxring *rxr;
+	struct vmxnet3_comp_ring *rxc;
+	struct vmxnet3_rxcompdesc *rxcd;
+	int idx, eof;
+
+	sc = rxq->vxrxq_sc;
+	rxc = &rxq->vxrxq_comp_ring;
+
+	do {
+		rxcd = &rxc->vxcr_u.rxcd[rxc->vxcr_next];
+		if (rxcd->gen != rxc->vxcr_gen)
+			break;		/* Not expected. */
+		vmxnet3_barrier(sc, VMXNET3_BARRIER_RD);
+
+		if (++rxc->vxcr_next == rxc->vxcr_ndesc) {
+			rxc->vxcr_next = 0;
+			rxc->vxcr_gen ^= 1;
+		}
+
+		idx = rxcd->rxd_idx;
+		eof = rxcd->eop;
+		if (rxcd->qid < sc->vmx_nrxqueues)
+			rxr = &rxq->vxrxq_cmd_ring[0];
+		else
+			rxr = &rxq->vxrxq_cmd_ring[1];
+		vmxnet3_rxq_eof_discard(rxq, rxr, idx);
+	} while (!eof);
+}
+
+void
+vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m)
+{
+	if (rxcd->no_csum)
+		return;
+
+	if (rxcd->ipv4) {
+		m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
+		if (rxcd->ipcsum_ok == 0)
+			m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
+	}
+
+	if (rxcd->fragment)
+		return;
+
+	if (rxcd->tcp) {
+		m->m_pkthdr.csum_flags |=
+		    rxcd->ipv4 ? M_CSUM_TCPv4 : M_CSUM_TCPv6;
+		if ((rxcd->csum_ok) == 0)
+			m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD;
+	}
+
+	if (rxcd->udp) {
+		m->m_pkthdr.csum_flags |=
+		    rxcd->ipv4 ? M_CSUM_UDPv4 : M_CSUM_UDPv6 ;
+		if ((rxcd->csum_ok) == 0)
+			m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD;
+	}
+}
+
+void
+vmxnet3_rxq_input(struct vmxnet3_rxqueue *rxq,
+    struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m)
+{
+	struct vmxnet3_softc *sc;
+	struct ifnet *ifp;
+
+	sc = rxq->vxrxq_sc;
+	ifp = &sc->vmx_ethercom.ec_if;
+
+	if (rxcd->error) {
+		rxq->vxrxq_stats.vmrxs_ierrors++;
+		m_freem(m);
+		return;
+	}
+
+	if (!rxcd->no_csum)
+		vmxnet3_rx_csum(rxcd, m);
+	if (rxcd->vlan) {
+		VLAN_INPUT_TAG(ifp, m, rxcd->vtag,
+		    rxq->vxrxq_stats.vmrxs_ierrors++;
+		    m_freem(m);
+		    return);
+	}
+
+	rxq->vxrxq_stats.vmrxs_ipackets++;
+	rxq->vxrxq_stats.vmrxs_ibytes += m->m_pkthdr.len;
+
+	if_percpuq_enqueue(ifp->if_percpuq, m);
+}
+
+void
+vmxnet3_rxq_eof(struct vmxnet3_rxqueue *rxq)
+{
+	struct vmxnet3_softc *sc;
+	struct ifnet *ifp;
+	struct vmxnet3_rxring *rxr;
+	struct vmxnet3_comp_ring *rxc;
+	struct vmxnet3_rxdesc *rxd;
+	struct vmxnet3_rxcompdesc *rxcd;
+	struct mbuf *m, *m_head, *m_tail;
+	int idx, length;
+
+	sc = rxq->vxrxq_sc;
+	ifp = &sc->vmx_ethercom.ec_if;
+	rxc = &rxq->vxrxq_comp_ring;
+
+	VMXNET3_RXQ_LOCK_ASSERT(rxq);
 
 	if ((ifp->if_flags & IFF_RUNNING) == 0)
-		return 0;
-	if (READ_BAR1(sc, VMXNET3_BAR1_INTR) == 0)
-		return 0;
-	if (sc->sc_ds->event)
+		return;
+
+	m_head = rxq->vxrxq_mhead;
+	rxq->vxrxq_mhead = NULL;
+	m_tail = rxq->vxrxq_mtail;
+	rxq->vxrxq_mtail = NULL;
+	KASSERT(m_head == NULL || m_tail != NULL);
+
+	for (;;) {
+		rxcd = &rxc->vxcr_u.rxcd[rxc->vxcr_next];
+		if (rxcd->gen != rxc->vxcr_gen) {
+			rxq->vxrxq_mhead = m_head;
+			rxq->vxrxq_mtail = m_tail;
+			break;
+		}
+		vmxnet3_barrier(sc, VMXNET3_BARRIER_RD);
+
+		if (++rxc->vxcr_next == rxc->vxcr_ndesc) {
+			rxc->vxcr_next = 0;
+			rxc->vxcr_gen ^= 1;
+		}
+
+		idx = rxcd->rxd_idx;
+		length = rxcd->len;
+		if (rxcd->qid < sc->vmx_nrxqueues)
+			rxr = &rxq->vxrxq_cmd_ring[0];
+		else
+			rxr = &rxq->vxrxq_cmd_ring[1];
+		rxd = &rxr->vxrxr_rxd[idx];
+
+		m = rxr->vxrxr_rxbuf[idx].vrxb_m;
+		KASSERT(m != NULL);
+
+		/*
+		 * The host may skip descriptors. We detect this when this
+		 * descriptor does not match the previous fill index. Catch
+		 * up with the host now.
+		 */
+		if (__predict_false(rxr->vxrxr_fill != idx)) {
+			while (rxr->vxrxr_fill != idx) {
+				rxr->vxrxr_rxd[rxr->vxrxr_fill].gen =
+				    rxr->vxrxr_gen;
+				vmxnet3_rxr_increment_fill(rxr);
+			}
+		}
+
+		if (rxcd->sop) {
+			/* start of frame w/o head buffer */
+			KASSERT(rxd->btype == VMXNET3_BTYPE_HEAD);
+			/* start of frame not in ring 0 */
+			KASSERT(rxr == &rxq->vxrxq_cmd_ring[0]);
+			/* duplicate start of frame? */
+			KASSERT(m_head == NULL);
+
+			if (length == 0) {
+				/* Just ignore this descriptor. */
+				vmxnet3_rxq_eof_discard(rxq, rxr, idx);
+				goto nextp;
+			}
+
+			if (vmxnet3_newbuf(sc, rxr) != 0) {
+				rxq->vxrxq_stats.vmrxs_iqdrops++;
+				vmxnet3_rxq_eof_discard(rxq, rxr, idx);
+				if (!rxcd->eop)
+					vmxnet3_rxq_discard_chain(rxq);
+				goto nextp;
+			}
+
+			m_set_rcvif(m, ifp);
+			m->m_pkthdr.len = m->m_len = length;
+			m->m_pkthdr.csum_flags = 0;
+			m_head = m_tail = m;
+
+		} else {
+			/* non start of frame w/o body buffer */
+			KASSERT(rxd->btype == VMXNET3_BTYPE_BODY);
+			/* frame not started? */
+			KASSERT(m_head != NULL);
+
+			if (vmxnet3_newbuf(sc, rxr) != 0) {
+				rxq->vxrxq_stats.vmrxs_iqdrops++;
+				vmxnet3_rxq_eof_discard(rxq, rxr, idx);
+				if (!rxcd->eop)
+					vmxnet3_rxq_discard_chain(rxq);
+				m_freem(m_head);
+				m_head = m_tail = NULL;
+				goto nextp;
+			}
+
+			m->m_len = length;
+			m_head->m_pkthdr.len += length;
+			m_tail->m_next = m;
+			m_tail = m;
+		}
+
+		if (rxcd->eop) {
+			vmxnet3_rxq_input(rxq, rxcd, m_head);
+			m_head = m_tail = NULL;
+
+			/* Must recheck after dropping the Rx lock. */
+			if ((ifp->if_flags & IFF_RUNNING) == 0)
+				break;
+		}
+
+nextp:
+		if (__predict_false(rxq->vxrxq_rs->update_rxhead)) {
+			int qid = rxcd->qid;
+			bus_size_t r;
+
+			idx = (idx + 1) % rxr->vxrxr_ndesc;
+			if (qid >= sc->vmx_nrxqueues) {
+				qid -= sc->vmx_nrxqueues;
+				r = VMXNET3_BAR0_RXH2(qid);
+			} else
+				r = VMXNET3_BAR0_RXH1(qid);
+			vmxnet3_write_bar0(sc, r, idx);
+		}
+	}
+}
+
+int
+vmxnet3_legacy_intr(void *xsc)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_rxqueue *rxq;
+	struct vmxnet3_txqueue *txq;
+
+	sc = xsc;
+	rxq = &sc->vmx_rxq[0];
+	txq = &sc->vmx_txq[0];
+
+	if (sc->vmx_intr_type == VMXNET3_IT_LEGACY) {
+		if (vmxnet3_read_bar1(sc, VMXNET3_BAR1_INTR) == 0)
+			return (0);
+	}
+	if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE)
+		vmxnet3_disable_all_intrs(sc);
+
+	if (sc->vmx_ds->event != 0)
 		vmxnet3_evintr(sc);
-	vmxnet3_rxintr(sc, &sc->sc_rxq[0]);
-	vmxnet3_txintr(sc, &sc->sc_txq[0]);
-#ifdef VMXNET3_STAT
-	vmxstat.intr++;
-#endif
-	vmxnet3_enable_intr(sc, 0);
-	return 1;
+
+	VMXNET3_RXQ_LOCK(rxq);
+	vmxnet3_rxq_eof(rxq);
+	VMXNET3_RXQ_UNLOCK(rxq);
+
+	VMXNET3_TXQ_LOCK(txq);
+	vmxnet3_txq_eof(txq);
+	vmxnet3_start_locked(&sc->vmx_ethercom.ec_if);
+	VMXNET3_TXQ_UNLOCK(txq);
+
+	vmxnet3_enable_all_intrs(sc);
+
+	return (1);
+}
+
+int
+vmxnet3_txq_intr(void *xtxq)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_txqueue *txq;
+
+	txq = xtxq;
+	sc = txq->vxtxq_sc;
+
+	if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE)
+		vmxnet3_disable_intr(sc, txq->vxtxq_intr_idx);
+
+	VMXNET3_TXQ_LOCK(txq);
+	vmxnet3_txq_eof(txq);
+	vmxnet3_start_locked(&sc->vmx_ethercom.ec_if);
+	VMXNET3_TXQ_UNLOCK(txq);
+
+	vmxnet3_enable_intr(sc, txq->vxtxq_intr_idx);
+
+	return (1);
+}
+
+int
+vmxnet3_rxq_intr(void *xrxq)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_rxqueue *rxq;
+
+	rxq = xrxq;
+	sc = rxq->vxrxq_sc;
+
+	if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE)
+		vmxnet3_disable_intr(sc, rxq->vxrxq_intr_idx);
+
+	VMXNET3_RXQ_LOCK(rxq);
+	vmxnet3_rxq_eof(rxq);
+	VMXNET3_RXQ_UNLOCK(rxq);
+
+	vmxnet3_enable_intr(sc, rxq->vxrxq_intr_idx);
+
+	return (1);
+}
+
+int
+vmxnet3_event_intr(void *xsc)
+{
+	struct vmxnet3_softc *sc;
+
+	sc = xsc;
+
+	if (sc->vmx_intr_mask_mode == VMXNET3_IMM_ACTIVE)
+		vmxnet3_disable_intr(sc, sc->vmx_event_intr_idx);
+
+	if (sc->vmx_ds->event != 0)
+		vmxnet3_evintr(sc);
+
+	vmxnet3_enable_intr(sc, sc->vmx_event_intr_idx);
+
+	return (1);
 }
 
 void
-vmxnet3_evintr(struct vmxnet3_softc *sc)
+vmxnet3_txstop(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *txq)
 {
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	u_int event = sc->sc_ds->event;
-	struct vmxnet3_txq_shared *ts;
-	struct vmxnet3_rxq_shared *rs;
+	struct vmxnet3_txring *txr;
+	struct vmxnet3_txbuf *txb;
+	int i;
 
-	/* Clear events. */
-	WRITE_BAR1(sc, VMXNET3_BAR1_EVENT, event);
+	txr = &txq->vxtxq_cmd_ring;
+
+	for (i = 0; i < txr->vxtxr_ndesc; i++) {
+		txb = &txr->vxtxr_txbuf[i];
+
+		if (txb->vtxb_m == NULL)
+			continue;
+
+		bus_dmamap_sync(sc->vmx_dmat, txb->vtxb_dmamap,
+		    0, txb->vtxb_dmamap->dm_mapsize,
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->vmx_dmat, txb->vtxb_dmamap);
+		m_freem(txb->vtxb_m);
+		txb->vtxb_m = NULL;
+	}
+}
+
+void
+vmxnet3_rxstop(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rxq)
+{
+	struct vmxnet3_rxring *rxr;
+	struct vmxnet3_rxbuf *rxb;
+	int i, j;
+
+	if (rxq->vxrxq_mhead != NULL) {
+		m_freem(rxq->vxrxq_mhead);
+		rxq->vxrxq_mhead = NULL;
+		rxq->vxrxq_mtail = NULL;
+	}
+
+	for (i = 0; i < VMXNET3_RXRINGS_PERQ; i++) {
+		rxr = &rxq->vxrxq_cmd_ring[i];
+
+		for (j = 0; j < rxr->vxrxr_ndesc; j++) {
+			rxb = &rxr->vxrxr_rxbuf[j];
+
+			if (rxb->vrxb_m == NULL)
+				continue;
+
+			bus_dmamap_sync(sc->vmx_dmat, rxb->vrxb_dmamap,
+			    0, rxb->vrxb_dmamap->dm_mapsize,
+			    BUS_DMASYNC_POSTREAD);
+			bus_dmamap_unload(sc->vmx_dmat, rxb->vrxb_dmamap);
+			m_freem(rxb->vrxb_m);
+			rxb->vrxb_m = NULL;
+		}
+	}
+}
+
+void
+vmxnet3_stop_rendezvous(struct vmxnet3_softc *sc)
+{
+	struct vmxnet3_rxqueue *rxq;
+	struct vmxnet3_txqueue *txq;
+	int i;
+
+	for (i = 0; i < sc->vmx_nrxqueues; i++) {
+		rxq = &sc->vmx_rxq[i];
+		VMXNET3_RXQ_LOCK(rxq);
+		VMXNET3_RXQ_UNLOCK(rxq);
+	}
+
+	for (i = 0; i < sc->vmx_ntxqueues; i++) {
+		txq = &sc->vmx_txq[i];
+		VMXNET3_TXQ_LOCK(txq);
+		VMXNET3_TXQ_UNLOCK(txq);
+	}
+}
+
+void
+vmxnet3_stop_locked(struct vmxnet3_softc *sc)
+{
+	struct ifnet *ifp;
+	int q;
+
+	ifp = &sc->vmx_ethercom.ec_if;
+	VMXNET3_CORE_LOCK_ASSERT(sc);
+
+	ifp->if_flags &= ~IFF_RUNNING;
+	sc->vmx_link_active = 0;
+	callout_stop(&sc->vmx_tick);
+
+	/* Disable interrupts. */
+	vmxnet3_disable_all_intrs(sc);
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_DISABLE);
+
+	vmxnet3_stop_rendezvous(sc);
+
+	for (q = 0; q < sc->vmx_ntxqueues; q++)
+		vmxnet3_txstop(sc, &sc->vmx_txq[q]);
+	for (q = 0; q < sc->vmx_nrxqueues; q++)
+		vmxnet3_rxstop(sc, &sc->vmx_rxq[q]);
+
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_RESET);
+}
+
+void
+vmxnet3_stop(struct ifnet *ifp, int disable)
+{
+	struct vmxnet3_softc *sc = ifp->if_softc;
+
+	VMXNET3_CORE_LOCK(sc);
+	vmxnet3_stop_locked(sc);
+	VMXNET3_CORE_UNLOCK(sc);
+}
+
+void
+vmxnet3_txinit(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *txq)
+{
+	struct vmxnet3_txring *txr;
+	struct vmxnet3_comp_ring *txc;
+
+	txr = &txq->vxtxq_cmd_ring;
+	txr->vxtxr_head = 0;
+	txr->vxtxr_next = 0;
+	txr->vxtxr_gen = VMXNET3_INIT_GEN;
+	memset(txr->vxtxr_txd, 0,
+	    txr->vxtxr_ndesc * sizeof(struct vmxnet3_txdesc));
+
+	txc = &txq->vxtxq_comp_ring;
+	txc->vxcr_next = 0;
+	txc->vxcr_gen = VMXNET3_INIT_GEN;
+	memset(txc->vxcr_u.txcd, 0,
+	    txc->vxcr_ndesc * sizeof(struct vmxnet3_txcompdesc));
+}
+
+int
+vmxnet3_rxinit(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rxq)
+{
+	struct vmxnet3_rxring *rxr;
+	struct vmxnet3_comp_ring *rxc;
+	int i, populate, idx, error;
+
+	/* LRO and jumbo frame is not supported yet */
+	populate = 1;
+
+	for (i = 0; i < populate; i++) {
+		rxr = &rxq->vxrxq_cmd_ring[i];
+		rxr->vxrxr_fill = 0;
+		rxr->vxrxr_gen = VMXNET3_INIT_GEN;
+		memset(rxr->vxrxr_rxd, 0,
+		    rxr->vxrxr_ndesc * sizeof(struct vmxnet3_rxdesc));
+
+		for (idx = 0; idx < rxr->vxrxr_ndesc; idx++) {
+			error = vmxnet3_newbuf(sc, rxr);
+			if (error)
+				return (error);
+		}
+	}
+
+	for (/**/; i < VMXNET3_RXRINGS_PERQ; i++) {
+		rxr = &rxq->vxrxq_cmd_ring[i];
+		rxr->vxrxr_fill = 0;
+		rxr->vxrxr_gen = 0;
+		memset(rxr->vxrxr_rxd, 0,
+		    rxr->vxrxr_ndesc * sizeof(struct vmxnet3_rxdesc));
+	}
+
+	rxc = &rxq->vxrxq_comp_ring;
+	rxc->vxcr_next = 0;
+	rxc->vxcr_gen = VMXNET3_INIT_GEN;
+	memset(rxc->vxcr_u.rxcd, 0,
+	    rxc->vxcr_ndesc * sizeof(struct vmxnet3_rxcompdesc));
+
+	return (0);
+}
+
+int
+vmxnet3_reinit_queues(struct vmxnet3_softc *sc)
+{
+	device_t dev;
+	int q, error;
+	dev = sc->vmx_dev;
+
+	for (q = 0; q < sc->vmx_ntxqueues; q++)
+		vmxnet3_txinit(sc, &sc->vmx_txq[q]);
+
+	for (q = 0; q < sc->vmx_nrxqueues; q++) {
+		error = vmxnet3_rxinit(sc, &sc->vmx_rxq[q]);
+		if (error) {
+			device_printf(dev, "cannot populate Rx queue %d\n", q);
+			return (error);
+		}
+	}
+
+	return (0);
+}
+
+int
+vmxnet3_enable_device(struct vmxnet3_softc *sc)
+{
+	int q;
+
+	if (vmxnet3_read_cmd(sc, VMXNET3_CMD_ENABLE) != 0) {
+		device_printf(sc->vmx_dev, "device enable command failed!\n");
+		return (1);
+	}
+
+	/* Reset the Rx queue heads. */
+	for (q = 0; q < sc->vmx_nrxqueues; q++) {
+		vmxnet3_write_bar0(sc, VMXNET3_BAR0_RXH1(q), 0);
+		vmxnet3_write_bar0(sc, VMXNET3_BAR0_RXH2(q), 0);
+	}
+
+	return (0);
+}
+
+void
+vmxnet3_reinit_rxfilters(struct vmxnet3_softc *sc)
+{
+
+	vmxnet3_set_rxfilter(sc);
+
+	memset(sc->vmx_ds->vlan_filter, 0, sizeof(sc->vmx_ds->vlan_filter));
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_VLAN_FILTER);
+}
+
+int
+vmxnet3_reinit(struct vmxnet3_softc *sc)
+{
+
+	vmxnet3_set_lladdr(sc);
+	vmxnet3_reinit_shared_data(sc);
+
+	if (vmxnet3_reinit_queues(sc) != 0)
+		return (ENXIO);
+
+	if (vmxnet3_enable_device(sc) != 0)
+		return (ENXIO);
+
+	vmxnet3_reinit_rxfilters(sc);
+
+	return (0);
+}
+
+
+
+
+
+int
+vmxnet3_init_locked(struct vmxnet3_softc *sc)
+{
+	struct ifnet *ifp = &sc->vmx_ethercom.ec_if;
+	int error;
+
+	if (ifp->if_flags & IFF_RUNNING)
+		return 0;
+
+	vmxnet3_stop_locked(sc);
+
+	error = vmxnet3_reinit(sc);
+	if (error) {
+		vmxnet3_stop_locked(sc);
+		return (error);
+	}
+
+	ifp->if_flags |= IFF_RUNNING;
+	vmxnet3_link_status(sc);
+
+	vmxnet3_enable_all_intrs(sc);
+	callout_reset(&sc->vmx_tick, hz, vmxnet3_tick, sc);
+
+	return (0);
+}
+
+int
+vmxnet3_init(struct ifnet *ifp)
+{
+	struct vmxnet3_softc *sc = ifp->if_softc;
+	int error;
+
+	VMXNET3_CORE_LOCK(sc);
+	error = vmxnet3_init_locked(sc);
+	VMXNET3_CORE_UNLOCK(sc);
+
+	return (error);
+}
+
+int
+vmxnet3_txq_offload_ctx(struct vmxnet3_txqueue *txq, struct mbuf *m,
+    int *start, int *csum_start)
+{
+	struct ether_header *eh;
+	struct mbuf *mp;
+	int offset, csum_off, iphl, offp;
+	bool v4;
+
+	eh = mtod(m, struct ether_header *);
+	switch (htons(eh->ether_type)) {
+	case ETHERTYPE_IP:
+	case ETHERTYPE_IPV6:
+		offset = ETHER_HDR_LEN;
+		break;
+	case ETHERTYPE_VLAN:
+		offset = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	if ((m->m_pkthdr.csum_flags &
+	    (M_CSUM_TSOv4|M_CSUM_UDPv4|M_CSUM_TCPv4)) != 0) {
+		iphl = M_CSUM_DATA_IPv4_IPHL(m->m_pkthdr.csum_data);
+		v4 = true;
+	} else {
+		iphl = M_CSUM_DATA_IPv6_HL(m->m_pkthdr.csum_data);
+		v4 = false;
+	}
+	*start = offset + iphl;
+
+	if (m->m_pkthdr.csum_flags &
+	    (M_CSUM_TCPv4 | M_CSUM_TCPv4 | M_CSUM_TSOv4 | M_CSUM_TSOv6)) {
+		csum_off = offsetof(struct tcphdr, th_sum);
+	} else {
+		csum_off = offsetof(struct udphdr, uh_sum);
+	}
+
+	*csum_start = *start + csum_off;
+	mp = m_pulldown(m, 0, *csum_start + 2, &offp);
+
+	if (m->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) {
+		struct tcphdr *tcp;
+
+		txq->vxtxq_stats.vmtxs_tso++;
+		tcp = (void *)(mtod(mp, char *) + offp + *start);
+
+		if (v4) {
+			struct ip *ip;
+
+			ip = (void *)(mtod(mp, char *) + offp + offset);
+			tcp->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
+			    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+		} else {
+			struct ip6_hdr *ip6;
+
+			ip6 = (void *)(mtod(mp, char *) + offp + offset);
+			tcp->th_sum = in6_cksum_phdr(&ip6->ip6_src,
+			    &ip6->ip6_dst, 0, htonl(IPPROTO_TCP));
+		}
+
+		/*
+		 * For TSO, the size of the protocol header is also
+		 * included in the descriptor header size.
+		 */
+		*start += (tcp->th_off << 2);
+	} else
+		txq->vxtxq_stats.vmtxs_csum++;
+
+	return (0);
+}
+
+int
+vmxnet3_txq_load_mbuf(struct vmxnet3_txqueue *txq, struct mbuf **m0,
+    bus_dmamap_t dmap)
+{
+	struct mbuf *m;
+	bus_dma_tag_t tag;
+	int error;
 
-	/* Link state change? */
-	if (event & VMXNET3_EVENT_LINK)
-		vmxnet3_link_state(sc);
+	m = *m0;
+	tag = txq->vxtxq_sc->vmx_dmat;
 
-	/* Queue error? */
-	if (event & (VMXNET3_EVENT_TQERROR | VMXNET3_EVENT_RQERROR)) {
-		WRITE_CMD(sc, VMXNET3_CMD_GET_STATUS);
+	error = bus_dmamap_load_mbuf(tag, dmap, m, BUS_DMA_NOWAIT);
+	if (error == 0 || error != EFBIG)
+		return (error);
+
+	m = m_defrag(m, M_NOWAIT);
+	if (m != NULL) {
+		*m0 = m;
+		error = bus_dmamap_load_mbuf(tag, dmap, m, BUS_DMA_NOWAIT);
+	} else
+		error = ENOBUFS;
 
-		ts = sc->sc_txq[0].ts;
-		if (ts->stopped)
-			printf("%s: TX error 0x%x\n", ifp->if_xname, ts->error);
-		rs = sc->sc_rxq[0].rs;
-		if (rs->stopped)
-			printf("%s: RX error 0x%x\n", ifp->if_xname, rs->error);
-		vmxnet3_reset(ifp);
-	}
+	if (error) {
+		m_freem(*m0);
+		*m0 = NULL;
+		txq->vxtxq_sc->vmx_stats.vmst_defrag_failed++;
+	} else
+		txq->vxtxq_sc->vmx_stats.vmst_defragged++;
 
-	if (event & VMXNET3_EVENT_DIC)
-		printf("%s: device implementation change event\n",
-		    ifp->if_xname);
-	if (event & VMXNET3_EVENT_DEBUG)
-		printf("%s: debug event\n", ifp->if_xname);
+	return (error);
 }
 
 void
-vmxnet3_txintr(struct vmxnet3_softc *sc, struct vmxnet3_txqueue *tq)
+vmxnet3_txq_unload_mbuf(struct vmxnet3_txqueue *txq, bus_dmamap_t dmap)
 {
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	struct vmxnet3_comp_ring *comp_ring = &tq->comp_ring;
-	struct vmxnet3_txcompdesc *txcd;
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	u_int sop;
 
-	for (;;) {
-		txcd = &comp_ring->txcd[comp_ring->next];
+	bus_dmamap_unload(txq->vxtxq_sc->vmx_dmat, dmap);
+}
 
-		if (le32toh((txcd->txc_word3 >> VMXNET3_TXC_GEN_S) &
-		    VMXNET3_TXC_GEN_M) != comp_ring->gen)
-			break;
+int
+vmxnet3_txq_encap(struct vmxnet3_txqueue *txq, struct mbuf **m0)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_txring *txr;
+	struct vmxnet3_txdesc *txd, *sop;
+	struct mbuf *m;
+	bus_dmamap_t dmap;
+	bus_dma_segment_t *segs;
+	struct m_tag *mtag;
+	int i, gen, start, csum_start, nsegs, error;
 
-		comp_ring->next++;
-		if (comp_ring->next == NTXCOMPDESC) {
-			comp_ring->next = 0;
-			comp_ring->gen ^= 1;
+	sc = txq->vxtxq_sc;
+	start = 0;
+	txd = NULL;
+	txr = &txq->vxtxq_cmd_ring;
+	dmap = txr->vxtxr_txbuf[txr->vxtxr_head].vtxb_dmamap;
+
+	error = vmxnet3_txq_load_mbuf(txq, m0, dmap);
+	if (error)
+		return (error);
+
+	nsegs = dmap->dm_nsegs;
+	segs = dmap->dm_segs;
+
+	m = *m0;
+	KASSERT(m->m_flags & M_PKTHDR);
+	KASSERT(nsegs <= VMXNET3_TX_MAXSEGS);
+
+	if (vmxnet3_txring_avail(txr) < nsegs) {
+		txq->vxtxq_stats.vmtxs_full++;
+		vmxnet3_txq_unload_mbuf(txq, dmap);
+		return (ENOSPC);
+	} else if (m->m_pkthdr.csum_flags & VMXNET3_CSUM_ALL_OFFLOAD) {
+		error = vmxnet3_txq_offload_ctx(txq, m, &start, &csum_start);
+		if (error) {
+			txq->vxtxq_stats.vmtxs_offload_failed++;
+			vmxnet3_txq_unload_mbuf(txq, dmap);
+			m_freem(m);
+			*m0 = NULL;
+			return (error);
 		}
+	}
 
-		sop = ring->next;
-		if (ring->m[sop] == NULL)
-			panic("vmxnet3_txintr");
-		m_freem(ring->m[sop]);
-		ring->m[sop] = NULL;
-		bus_dmamap_unload(sc->sc_dmat, ring->dmap[sop]);
-		ring->next = (le32toh((txcd->txc_word0 >>
-		    VMXNET3_TXC_EOPIDX_S) & VMXNET3_TXC_EOPIDX_M) + 1)
-		    % NTXDESC;
+	txr->vxtxr_txbuf[txr->vxtxr_head].vtxb_m = m;
+	sop = &txr->vxtxr_txd[txr->vxtxr_head];
+	gen = txr->vxtxr_gen ^ 1;	/* Owned by cpu (yet) */
+
+	for (i = 0; i < nsegs; i++) {
+		txd = &txr->vxtxr_txd[txr->vxtxr_head];
+
+		txd->addr = segs[i].ds_addr;
+		txd->len = segs[i].ds_len;
+		txd->gen = gen;
+		txd->dtype = 0;
+		txd->offload_mode = VMXNET3_OM_NONE;
+		txd->offload_pos = 0;
+		txd->hlen = 0;
+		txd->eop = 0;
+		txd->compreq = 0;
+		txd->vtag_mode = 0;
+		txd->vtag = 0;
+
+		if (++txr->vxtxr_head == txr->vxtxr_ndesc) {
+			txr->vxtxr_head = 0;
+			txr->vxtxr_gen ^= 1;
+		}
+		gen = txr->vxtxr_gen;
+	}
+	txd->eop = 1;
+	txd->compreq = 1;
 
-		ifp->if_flags &= ~IFF_OACTIVE;
+	if ((mtag = VLAN_OUTPUT_TAG(&sc->vmx_ethercom, m)) != NULL) {
+		sop->vtag_mode = 1;
+		sop->vtag = VLAN_TAG_VALUE(mtag);
+	}
+
+	if (m->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) {
+		sop->offload_mode = VMXNET3_OM_TSO;
+		sop->hlen = start;
+		sop->offload_pos = m->m_pkthdr.segsz;
+	} else if (m->m_pkthdr.csum_flags & (VMXNET3_CSUM_OFFLOAD |
+	    VMXNET3_CSUM_OFFLOAD_IPV6)) {
+		sop->offload_mode = VMXNET3_OM_CSUM;
+		sop->hlen = start;
+		sop->offload_pos = csum_start;
+	}
+
+	/* Finally, change the ownership. */
+	vmxnet3_barrier(sc, VMXNET3_BARRIER_WR);
+	sop->gen ^= 1;
+
+	txq->vxtxq_ts->npending += nsegs;
+	if (txq->vxtxq_ts->npending >= txq->vxtxq_ts->intr_threshold) {
+		txq->vxtxq_ts->npending = 0;
+		vmxnet3_write_bar0(sc, VMXNET3_BAR0_TXH(txq->vxtxq_id),
+		    txr->vxtxr_head);
 	}
-	if (ring->head == ring->next)
-		ifp->if_timer = 0;
-	vmxnet3_start(ifp);
+
+	return (0);
 }
 
 void
-vmxnet3_rxintr(struct vmxnet3_softc *sc, struct vmxnet3_rxqueue *rq)
+vmxnet3_start_locked(struct ifnet *ifp)
 {
-	struct vmxnet3_comp_ring *comp_ring = &rq->comp_ring;
-	struct vmxnet3_rxring *ring;
-	struct vmxnet3_rxdesc *rxd;
-	struct vmxnet3_rxcompdesc *rxcd;
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	struct mbuf *m;
-	int idx, len;
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_txqueue *txq;
+	struct vmxnet3_txring *txr;
+	struct mbuf *m_head;
+	int tx;
+
+	sc = ifp->if_softc;
+	txq = &sc->vmx_txq[0];
+	txr = &txq->vxtxq_cmd_ring;
+	tx = 0;
+
+	VMXNET3_TXQ_LOCK_ASSERT(txq);
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0 ||
+	    sc->vmx_link_active == 0)
+		return;
 
 	for (;;) {
-		rxcd = &comp_ring->rxcd[comp_ring->next];
-		if (le32toh((rxcd->rxc_word3 >> VMXNET3_RXC_GEN_S) &
-		    VMXNET3_RXC_GEN_M) != comp_ring->gen)
+		IFQ_POLL(&ifp->if_snd, m_head);
+		if (m_head == NULL)
 			break;
 
-		comp_ring->next++;
-		if (comp_ring->next == NRXCOMPDESC) {
-			comp_ring->next = 0;
-			comp_ring->gen ^= 1;
-		}
-
-		idx = le32toh((rxcd->rxc_word0 >> VMXNET3_RXC_IDX_S) &
-		    VMXNET3_RXC_IDX_M);
-		if (le32toh((rxcd->rxc_word0 >> VMXNET3_RXC_QID_S) &
-		    VMXNET3_RXC_QID_M) < NRXQUEUE)
-			ring = &rq->cmd_ring[0];
-		else
-			ring = &rq->cmd_ring[1];
-		rxd = &ring->rxd[idx];
-		len = le32toh((rxcd->rxc_word2 >> VMXNET3_RXC_LEN_S) &
-		    VMXNET3_RXC_LEN_M);
-		m = ring->m[idx];
-		ring->m[idx] = NULL;
-		bus_dmamap_unload(sc->sc_dmat, ring->dmap[idx]);
+		if (vmxnet3_txring_avail(txr) < VMXNET3_TX_MAXSEGS)
+			break;
 
-		if (m == NULL)
-			panic("NULL mbuf");
+		IFQ_DEQUEUE(&ifp->if_snd, m_head);
+		if (m_head == NULL)
+			break;
 
-		if (le32toh((rxd->rx_word2 >> VMXNET3_RX_BTYPE_S) &
-		    VMXNET3_RX_BTYPE_M) != VMXNET3_BTYPE_HEAD) {
-			m_freem(m);
-			goto skip_buffer;
-		}
-		if (le32toh(rxcd->rxc_word2 & VMXNET3_RXC_ERROR)) {
-			ifp->if_ierrors++;
-			m_freem(m);
-			goto skip_buffer;
-		}
-		if (len < VMXNET3_MIN_MTU) {
-			printf("%s: short packet (%d)\n", ifp->if_xname, len);
-			m_freem(m);
-			goto skip_buffer;
+		if (vmxnet3_txq_encap(txq, &m_head) != 0) {
+			if (m_head != NULL)
+				m_freem(m_head);
+			break;
 		}
 
-		ifp->if_ipackets++;
-		ifp->if_ibytes += len;
+		tx++;
+		bpf_mtap(ifp, m_head);
+	}
 
-		vmxnet3_rx_csum(rxcd, m);
-		m_set_rcvif(m, ifp);
-		m->m_pkthdr.len = m->m_len = len;
-		if (le32toh(rxcd->rxc_word2 & VMXNET3_RXC_VLAN)) {
-			VLAN_INPUT_TAG(ifp, m,
-			    le32toh((rxcd->rxc_word2 >>
-			    VMXNET3_RXC_VLANTAG_S) & VMXNET3_RXC_VLANTAG_M),
-			    m_freem(m); goto skip_buffer);
-		}
+	if (tx > 0)
+		txq->vxtxq_watchdog = VMXNET3_WATCHDOG_TIMEOUT;
+}
 
-		bpf_mtap(ifp, m);
 
-		if_percpuq_enqueue(ifp->if_percpuq, m);
+void
+vmxnet3_start(struct ifnet *ifp)
+{
+	struct vmxnet3_softc *sc;
+	struct vmxnet3_txqueue *txq;
 
-skip_buffer:
-#ifdef VMXNET3_STAT
-		vmxstat.rxdone = idx;
-#endif
-		if (rq->rs->update_rxhead) {
-			u_int qid = le32toh((rxcd->rxc_word0 >>
-			    VMXNET3_RXC_QID_S) & VMXNET3_RXC_QID_M);
-
-			idx = (idx + 1) % NRXDESC;
-			if (qid < NRXQUEUE) {
-				WRITE_BAR0(sc, VMXNET3_BAR0_RXH1(qid), idx);
-			} else {
-				qid -= NRXQUEUE;
-				WRITE_BAR0(sc, VMXNET3_BAR0_RXH2(qid), idx);
-			}
-		}
-	}
+	sc = ifp->if_softc;
+	txq = &sc->vmx_txq[0];
 
-	/* XXX Should we (try to) allocate buffers for ring 2 too? */
-	ring = &rq->cmd_ring[0];
-	for (;;) {
-		idx = ring->fill;
-		if (ring->m[idx])
-			return;
-		if (vmxnet3_getbuf(sc, ring))
-			return;
-	}
+	VMXNET3_TXQ_LOCK(txq);
+	vmxnet3_start_locked(ifp);
+	VMXNET3_TXQ_UNLOCK(txq);
 }
 
 void
-vmxnet3_iff(struct vmxnet3_softc *sc)
+vmxnet3_set_rxfilter(struct vmxnet3_softc *sc)
 {
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	struct ethercom *ec = &sc->sc_ethercom;
-	struct vmxnet3_driver_shared *ds = sc->sc_ds;
+	struct ifnet *ifp = &sc->vmx_ethercom.ec_if;
+	struct ethercom *ec = &sc->vmx_ethercom;
+	struct vmxnet3_driver_shared *ds = sc->vmx_ds;
 	struct ether_multi *enm;
 	struct ether_multistep step;
 	u_int mode;
@@ -821,7 +2815,7 @@ vmxnet3_iff(struct vmxnet3_softc *sc)
 	if (ISSET(ifp->if_flags, IFF_PROMISC) || ec->ec_multicnt > 682)
 		goto allmulti;
 
-	p = sc->sc_mcast;
+	p = sc->vmx_mcast;
 	ETHER_FIRST_MULTI(step, ec, enm);
 	while (enm != NULL) {
 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
@@ -844,7 +2838,7 @@ vmxnet3_iff(struct vmxnet3_softc *sc)
 
 	if (ec->ec_multicnt > 0) {
 		SET(mode, VMXNET3_RXMODE_MCAST);
-		ds->mcast_tablelen = p - sc->sc_mcast;
+		ds->mcast_tablelen = p - sc->vmx_mcast;
 	}
 
 	goto setit;
@@ -856,414 +2850,312 @@ allmulti:
 		SET(mode, VMXNET3_RXMODE_PROMISC);
 
 setit:
-	WRITE_CMD(sc, VMXNET3_CMD_SET_FILTER);
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_SET_FILTER);
 	ds->rxmode = mode;
-	WRITE_CMD(sc, VMXNET3_CMD_SET_RXMODE);
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_SET_RXMODE);
 }
 
 int
-vmxnet3_ifflags_cb(struct ethercom *ec)
+vmxnet3_change_mtu(struct vmxnet3_softc *sc, int mtu)
 {
+	struct ifnet *ifp = &sc->vmx_ethercom.ec_if;
+
+	if (mtu < VMXNET3_MIN_MTU || mtu > VMXNET3_MAX_MTU)
+		return EINVAL;
 
-	vmxnet3_iff((struct vmxnet3_softc *)ec->ec_if.if_softc);
+	if (ifp->if_flags & IFF_RUNNING) {
+		ifp->if_flags &= ~IFF_RUNNING;
+		vmxnet3_init_locked(sc);
+	}
 
 	return 0;
 }
 
-
-void
-vmxnet3_rx_csum(struct vmxnet3_rxcompdesc *rxcd, struct mbuf *m)
+int
+vmxnet3_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
-	if (le32toh(rxcd->rxc_word0 & VMXNET3_RXC_NOCSUM))
-		return;
+	struct vmxnet3_softc *sc = ifp->if_softc;
+	struct ifreq *ifr = (struct ifreq *)data;
+	int s, error = 0;
 
-	if (rxcd->rxc_word3 & VMXNET3_RXC_IPV4) {
-		m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
-		if ((rxcd->rxc_word3 & VMXNET3_RXC_IPSUM_OK) == 0)
-			m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
+	switch (cmd) {
+	case SIOCSIFMTU:
+		if (ifp->if_mtu != ifr->ifr_mtu) {
+			VMXNET3_CORE_LOCK(sc);
+			error = vmxnet3_change_mtu(sc, ifr->ifr_mtu);
+			VMXNET3_CORE_UNLOCK(sc);
+		}
+		break;
+	case SIOCSIFMEDIA:
+	case SIOCGIFMEDIA:
+		s = splnet();
+		error = ifmedia_ioctl(ifp, ifr, &sc->vmx_media, cmd);
+		splx(s);
+		break;
+	case SIOCGIFDATA:
+		for (int i = 0; i < sc->vmx_nrxqueues; i++) {
+			ifp->if_ipackets =
+			    sc->vmx_rxq[i].vxrxq_stats.vmrxs_ipackets;
+			ifp->if_iqdrops =
+			    sc->vmx_rxq[i].vxrxq_stats.vmrxs_iqdrops;
+			ifp->if_ierrors =
+			    sc->vmx_rxq[i].vxrxq_stats.vmrxs_ierrors;
+		}
+		for (int i = 0; i < sc->vmx_ntxqueues; i++) {
+			ifp->if_opackets =
+			    sc->vmx_txq[i].vxtxq_stats.vmtxs_opackets;
+			ifp->if_obytes =
+			    sc->vmx_txq[i].vxtxq_stats.vmtxs_obytes;
+			ifp->if_omcasts =
+			    sc->vmx_txq[i].vxtxq_stats.vmtxs_omcasts;
+		}
+		/* FALLTHROUGH */
+	default:
+		s = splnet();
+		error = ether_ioctl(ifp, cmd, data);
+		splx(s);
 	}
 
-	if (rxcd->rxc_word3 & VMXNET3_RXC_FRAGMENT)
-		return;
-
-	if (rxcd->rxc_word3 & VMXNET3_RXC_TCP) {
-		m->m_pkthdr.csum_flags |= M_CSUM_TCPv4;
-		if ((rxcd->rxc_word3 & VMXNET3_RXC_CSUM_OK) == 0)
-			m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD;
+	if (error == ENETRESET) {
+		VMXNET3_CORE_LOCK(sc);
+		if (ifp->if_flags & IFF_RUNNING)
+			vmxnet3_set_rxfilter(sc);
+		VMXNET3_CORE_UNLOCK(sc);
+		error = 0;
 	}
 
-	if (rxcd->rxc_word3 & VMXNET3_RXC_UDP) {
-		m->m_pkthdr.csum_flags |= M_CSUM_UDPv4;
-		if ((rxcd->rxc_word3 & VMXNET3_RXC_CSUM_OK) == 0)
-			m->m_pkthdr.csum_flags |= M_CSUM_TCP_UDP_BAD;
-	}
+	return error;
 }
 
 int
-vmxnet3_getbuf(struct vmxnet3_softc *sc, struct vmxnet3_rxring *ring)
+vmxnet3_ifflags_cb(struct ethercom *ec)
 {
-	int idx = ring->fill;
-	struct vmxnet3_rxdesc *rxd = &ring->rxd[idx];
-	struct mbuf *m;
-	int btype;
+	struct vmxnet3_softc *sc;
 
-	if (ring->m[idx])
-		panic("vmxnet3_getbuf: buffer has mbuf");
+	sc = ec->ec_if.if_softc;
 
-#if 1
-	/* XXX Don't allocate buffers for ring 2 for now. */
-	if (ring->rid != 0)
-		return -1;
-	btype = VMXNET3_BTYPE_HEAD;
-#else
-	if (ring->rid == 0)
-		btype = VMXNET3_BTYPE_HEAD;
-	else
-		btype = VMXNET3_BTYPE_BODY;
-#endif
+	VMXNET3_CORE_LOCK(sc);
+	vmxnet3_set_rxfilter(sc);
+	VMXNET3_CORE_UNLOCK(sc);
 
-	MGETHDR(m, M_DONTWAIT, MT_DATA);
-	if (m == NULL)
-		return -1;
+	return 0;
+}
 
-	MCLGET(m, M_DONTWAIT);
-	if ((m->m_flags & M_EXT) == 0) {
-		m_freem(m);
-		return -1;
-	}
+int
+vmxnet3_watchdog(struct vmxnet3_txqueue *txq)
+{
+	struct vmxnet3_softc *sc;
 
-	m->m_pkthdr.len = m->m_len = JUMBO_LEN;
-	m_adj(m, ETHER_ALIGN);
-	ring->m[idx] = m;
+	sc = txq->vxtxq_sc;
 
-	if (bus_dmamap_load_mbuf(sc->sc_dmat, ring->dmap[idx], m,
-	    BUS_DMA_NOWAIT))
-		panic("load mbuf");
-	rxd->rx_addr = htole64(DMAADDR(ring->dmap[idx]));
-	rxd->rx_word2 = htole32(((m->m_pkthdr.len & VMXNET3_RX_LEN_M) <<
-	    VMXNET3_RX_LEN_S) | ((btype & VMXNET3_RX_BTYPE_M) <<
-	    VMXNET3_RX_BTYPE_S) | ((ring->gen & VMXNET3_RX_GEN_M) <<
-	    VMXNET3_RX_GEN_S));
-	idx++;
-	if (idx == NRXDESC) {
-		idx = 0;
-		ring->gen ^= 1;
+	VMXNET3_TXQ_LOCK(txq);
+	if (txq->vxtxq_watchdog == 0 || --txq->vxtxq_watchdog) {
+		VMXNET3_TXQ_UNLOCK(txq);
+		return (0);
 	}
-	ring->fill = idx;
-#ifdef VMXNET3_STAT
-	vmxstat.rxfill = ring->fill;
-#endif
-	return 0;
+	VMXNET3_TXQ_UNLOCK(txq);
+
+	device_printf(sc->vmx_dev, "watchdog timeout on queue %d\n",
+	    txq->vxtxq_id);
+	return (1);
 }
 
 void
-vmxnet3_stop(struct ifnet *ifp, int disable)
+vmxnet3_refresh_host_stats(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_softc *sc = ifp->if_softc;
-	int queue;
 
-	if ((ifp->if_flags & IFF_RUNNING) == 0)
-		return;
+	vmxnet3_write_cmd(sc, VMXNET3_CMD_GET_STATS);
+}
 
-	vmxnet3_disable_all_intrs(sc);
-	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
-	ifp->if_timer = 0;
+void
+vmxnet3_tick(void *xsc)
+{
+	struct vmxnet3_softc *sc;
+	struct ifnet *ifp;
+	int i, timedout;
 
-	if (!disable)
-		return;
+	sc = xsc;
+	ifp = &sc->vmx_ethercom.ec_if;
+	timedout = 0;
+
+	VMXNET3_CORE_LOCK(sc);
 
-	WRITE_CMD(sc, VMXNET3_CMD_DISABLE);
+	vmxnet3_refresh_host_stats(sc);
 
-	for (queue = 0; queue < NTXQUEUE; queue++)
-		vmxnet3_txstop(sc, &sc->sc_txq[queue]);
-	for (queue = 0; queue < NRXQUEUE; queue++)
-		vmxnet3_rxstop(sc, &sc->sc_rxq[queue]);
+	for (i = 0; i < sc->vmx_ntxqueues; i++)
+		timedout |= vmxnet3_watchdog(&sc->vmx_txq[i]);
+
+	if (timedout != 0) {
+		ifp->if_flags &= ~IFF_RUNNING;
+		vmxnet3_init_locked(sc);
+	} else
+		callout_reset(&sc->vmx_tick, hz, vmxnet3_tick, sc);
+
+	VMXNET3_CORE_UNLOCK(sc);
 }
 
 void
-vmxnet3_reset(struct ifnet *ifp)
+vmxnet3_link_status(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_softc *sc = ifp->if_softc;
+	struct ifnet *ifp = &sc->vmx_ethercom.ec_if;
+	u_int x, link, speed;
+
+	x = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_LINK);
+	speed = x >> 16;
+	if (x & 1) {
+		sc->vmx_link_active = 1;
+		ifp->if_baudrate = IF_Mbps(speed);
+		link = LINK_STATE_UP;
+	} else {
+		sc->vmx_link_active = 0;
+		link = LINK_STATE_DOWN;
+	}
 
-	vmxnet3_stop(ifp, 1);
-	WRITE_CMD(sc, VMXNET3_CMD_RESET);
-	vmxnet3_init(ifp);
+	if_link_state_change(ifp, link);
 }
 
-int
-vmxnet3_init(struct ifnet *ifp)
+void
+vmxnet3_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
 {
 	struct vmxnet3_softc *sc = ifp->if_softc;
-	int queue;
-
-	if (ifp->if_flags & IFF_RUNNING)
-		return 0;
 
-	ifp->if_flags |= IFF_RUNNING;
-	ifp->if_flags &= ~IFF_OACTIVE;
+	vmxnet3_link_status(sc);
 
-	for (queue = 0; queue < NTXQUEUE; queue++)
-		vmxnet3_txinit(sc, &sc->sc_txq[queue]);
-	for (queue = 0; queue < NRXQUEUE; queue++)
-		vmxnet3_rxinit(sc, &sc->sc_rxq[queue]);
+	ifmr->ifm_status = IFM_AVALID;
+	ifmr->ifm_active = IFM_ETHER;
 
-	WRITE_CMD(sc, VMXNET3_CMD_ENABLE);
-	if (READ_BAR1(sc, VMXNET3_BAR1_CMD)) {
-		printf("%s: failed to initialize\n", ifp->if_xname);
-		vmxnet3_stop(ifp, 1);
-		return EIO;
+	VMXNET3_CORE_LOCK(sc);
+	if (ifp->if_link_state != LINK_STATE_UP) {
+		VMXNET3_CORE_UNLOCK(sc);
+		return;
 	}
 
-	for (queue = 0; queue < NRXQUEUE; queue++) {
-		WRITE_BAR0(sc, VMXNET3_BAR0_RXH1(queue), 0);
-		WRITE_BAR0(sc, VMXNET3_BAR0_RXH2(queue), 0);
-	}
+	ifmr->ifm_status |= IFM_ACTIVE;
 
-	vmxnet3_iff(sc);
-	vmxnet3_enable_all_intrs(sc);
-	vmxnet3_link_state(sc);
-	return 0;
+	if (ifp->if_baudrate >= IF_Gbps(10ULL))
+		ifmr->ifm_active |= IFM_10G_T;
+	VMXNET3_CORE_UNLOCK(sc);
 }
 
 int
-vmxnet3_change_mtu(struct vmxnet3_softc *sc, int mtu)
+vmxnet3_media_change(struct ifnet *ifp)
 {
-	struct vmxnet3_driver_shared *ds = sc->sc_ds;
-	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	int error;
-
-	if (mtu < VMXNET3_MIN_MTU || mtu > VMXNET3_MAX_MTU)
-		return EINVAL;
-	vmxnet3_stop(ifp, 1);
-	ifp->if_mtu = ds->mtu = mtu;
-	error = vmxnet3_init(ifp);
-	return error;
+	return 0;
 }
 
-int
-vmxnet3_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+void
+vmxnet3_set_lladdr(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_softc *sc = ifp->if_softc;
-	struct ifreq *ifr = (struct ifreq *)data;
-	int error = 0, s;
+	uint32_t ml, mh;
 
-	s = splnet();
+	ml  = sc->vmx_lladdr[0];
+	ml |= sc->vmx_lladdr[1] << 8;
+	ml |= sc->vmx_lladdr[2] << 16;
+	ml |= sc->vmx_lladdr[3] << 24;
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_MACL, ml);
+
+	mh  = sc->vmx_lladdr[4];
+	mh |= sc->vmx_lladdr[5] << 8;
+	vmxnet3_write_bar1(sc, VMXNET3_BAR1_MACH, mh);
+}
 
-	switch (cmd) {
-	case SIOCSIFMTU:
-		error = vmxnet3_change_mtu(sc, ifr->ifr_mtu);
-		break;
-	case SIOCSIFMEDIA:
-	case SIOCGIFMEDIA:
-		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
-		break;
-	default:
-		error = ether_ioctl(ifp, cmd, data);
-	}
+void
+vmxnet3_get_lladdr(struct vmxnet3_softc *sc)
+{
+	uint32_t ml, mh;
 
-	if (error == ENETRESET) {
-		if (ifp->if_flags & IFF_RUNNING)
-			vmxnet3_iff(sc);
-		error = 0;
-	}
+	ml = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_MACL);
+	mh = vmxnet3_read_cmd(sc, VMXNET3_CMD_GET_MACH);
 
-	splx(s);
-	return error;
+	sc->vmx_lladdr[0] = ml;
+	sc->vmx_lladdr[1] = ml >> 8;
+	sc->vmx_lladdr[2] = ml >> 16;
+	sc->vmx_lladdr[3] = ml >> 24;
+	sc->vmx_lladdr[4] = mh;
+	sc->vmx_lladdr[5] = mh >> 8;
 }
 
 void
-vmxnet3_start(struct ifnet *ifp)
+vmxnet3_enable_all_intrs(struct vmxnet3_softc *sc)
 {
-	struct vmxnet3_softc *sc = ifp->if_softc;
-	struct vmxnet3_txqueue *tq = &sc->sc_txq[0];
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	struct mbuf *m;
-	int n = 0;
-
-	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
-		return;
-
-	for (;;) {
-		IFQ_POLL(&ifp->if_snd, m);
-		if (m == NULL)
-			break;
-		if ((ring->next - ring->head - 1) % NTXDESC < NTXSEGS) {
-			ifp->if_flags |= IFF_OACTIVE;
-			break;
-		}
+	int i;
 
-		IFQ_DEQUEUE(&ifp->if_snd, m);
-		if (vmxnet3_load_mbuf(sc, m) != 0) {
-			ifp->if_oerrors++;
-			continue;
-		}
-		bpf_mtap(ifp, m);
+	sc->vmx_ds->ictrl &= ~VMXNET3_ICTRL_DISABLE_ALL;
+	for (i = 0; i < sc->vmx_nintrs; i++)
+		vmxnet3_enable_intr(sc, i);
+}
 
-		ifp->if_timer = 5;
-		ifp->if_opackets++;
-		n++;
-	}
+void
+vmxnet3_disable_all_intrs(struct vmxnet3_softc *sc)
+{
+	int i;
 
-	if (n > 0)
-		WRITE_BAR0(sc, VMXNET3_BAR0_TXH(0), ring->head);
-#ifdef VMXNET3_STAT
-	vmxstat.txhead = ring->head;
-	vmxstat.txdone = ring->next;
-	vmxstat.maxtxlen =
-	    max(vmxstat.maxtxlen, (ring->head - ring->next) % NTXDESC);
-#endif
+	sc->vmx_ds->ictrl |= VMXNET3_ICTRL_DISABLE_ALL;
+	for (i = 0; i < sc->vmx_nintrs; i++)
+		vmxnet3_disable_intr(sc, i);
 }
 
+
 int
-vmxnet3_load_mbuf(struct vmxnet3_softc *sc, struct mbuf *m)
+vmxnet3_dma_malloc(struct vmxnet3_softc *sc, bus_size_t size, bus_size_t align,
+    struct vmxnet3_dma_alloc *dma)
 {
-	struct vmxnet3_txqueue *tq = &sc->sc_txq[0];
-	struct vmxnet3_txring *ring = &tq->cmd_ring;
-	struct vmxnet3_txdesc *txd = NULL, *sop;
-	struct mbuf *mp;
-	struct m_tag *mtag;
-	struct ip *ip;
-	bus_dmamap_t map = ring->dmap[ring->head];
-	u_int hlen = ETHER_HDR_LEN, csum_off = 0;
-	int offp, gen, i;
-
-#if 0
-	if (m->m_pkthdr.csum_flags & M_CSUM_IPv4) {
-		printf("%s: IP checksum offloading is not supported\n",
-		    sc->sc_dev.dv_xname);
-		return -1;
-	}
-#endif
-	if (m->m_pkthdr.csum_flags & (M_CSUM_UDPv4|M_CSUM_TCPv4)) {
-		if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4)
-			csum_off = offsetof(struct tcphdr, th_sum);
-		else
-			csum_off = offsetof(struct udphdr, uh_sum);
-
-		mp = m_pulldown(m, hlen, sizeof(*ip), &offp);
-		if (mp == NULL)
-			return (-1);
+	bus_dma_tag_t t = sc->vmx_dmat;
+	bus_dma_segment_t *segs = dma->dma_segs;
+	int n, error;
 
-		ip = (struct ip *)(mp->m_data + offp);
-		hlen += ip->ip_hl << 2;
+	memset(dma, 0, sizeof(*dma));
 
-		mp = m_pulldown(m, 0, hlen + csum_off + 2, &offp);
-		if (mp == NULL)
-			return (-1);
+	error = bus_dmamem_alloc(t, size, align, 0, segs, 1, &n, BUS_DMA_NOWAIT);
+	if (error) {
+		aprint_error_dev(sc->vmx_dev, "bus_dmamem_alloc failed: %d\n", error);
+		goto fail1;
 	}
+	KASSERT(n == 1);
 
-	switch (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
-	case 0:
-		break;
-	case EFBIG:
-		mp = m_defrag(m, M_DONTWAIT);
-		if (mp != NULL) {
-			m = mp;
-			if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m,
-			    BUS_DMA_NOWAIT) == 0)
-				break;
-		}
-		/* FALLTHROUGH */
-	default:
-		m_freem(m);
-		return -1;
+	error = bus_dmamem_map(t, segs, 1, size, &dma->dma_vaddr, BUS_DMA_NOWAIT);
+	if (error) {
+		aprint_error_dev(sc->vmx_dev, "bus_dmamem_map failed: %d\n", error);
+		goto fail2;
 	}
 
-	ring->m[ring->head] = m;
-	sop = &ring->txd[ring->head];
-	gen = ring->gen ^ 1;		/* owned by cpu (yet) */
-	for (i = 0; i < map->dm_nsegs; i++) {
-		txd = &ring->txd[ring->head];
-		txd->tx_addr = htole64(map->dm_segs[i].ds_addr);
-		txd->tx_word2 = htole32(((map->dm_segs[i].ds_len &
-		    VMXNET3_TX_LEN_M) << VMXNET3_TX_LEN_S) |
-		    ((gen & VMXNET3_TX_GEN_M) << VMXNET3_TX_GEN_S));
-		txd->tx_word3 = 0;
-		ring->head++;
-		if (ring->head == NTXDESC) {
-			ring->head = 0;
-			ring->gen ^= 1;
-		}
-		gen = ring->gen;
+	error = bus_dmamap_create(t, size, 1, size, 0, BUS_DMA_NOWAIT, &dma->dma_map);
+	if (error) {
+		aprint_error_dev(sc->vmx_dev, "bus_dmamap_create failed: %d\n", error);
+		goto fail3;
 	}
-	if (txd != NULL)
-		txd->tx_word3 |= htole32(VMXNET3_TX_EOP | VMXNET3_TX_COMPREQ);
 
-	if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_ethercom, m)) != NULL) {
-		sop->tx_word3 |= htole32(VMXNET3_TX_VTAG_MODE);
-		sop->tx_word3 |= htole32((VLAN_TAG_VALUE(mtag) &
-		    VMXNET3_TX_VLANTAG_M) << VMXNET3_TX_VLANTAG_S);
-	}
-	if (m->m_pkthdr.csum_flags & (M_CSUM_UDPv4|M_CSUM_TCPv4)) {
-		sop->tx_word2 |= htole32(((hlen + csum_off) &
-		    VMXNET3_TX_OP_M) << VMXNET3_TX_OP_S);
-		sop->tx_word3 |= htole32(((hlen & VMXNET3_TX_HLEN_M) <<
-		    VMXNET3_TX_HLEN_S) | (VMXNET3_OM_CSUM << VMXNET3_TX_OM_S));
+	error = bus_dmamap_load(t, dma->dma_map, dma->dma_vaddr, size, NULL,
+	    BUS_DMA_NOWAIT);
+	if (error) {
+		aprint_error_dev(sc->vmx_dev, "bus_dmamap_load failed: %d\n", error);
+		goto fail4;
 	}
 
-	/* Change the ownership by flipping the "generation" bit */
-	sop->tx_word2 ^= htole32(VMXNET3_TX_GEN_M << VMXNET3_TX_GEN_S);
+	memset(dma->dma_vaddr, 0, size);
+	dma->dma_paddr = DMAADDR(dma->dma_map);
+	dma->dma_size = size;
 
 	return (0);
+fail4:
+	bus_dmamap_destroy(t, dma->dma_map);
+fail3:
+	bus_dmamem_unmap(t, dma->dma_vaddr, size);
+fail2:
+	bus_dmamem_free(t, segs, 1);
+fail1:
+	return (error);
 }
 
 void
-vmxnet3_watchdog(struct ifnet *ifp)
-{
-	int s;
-
-	printf("%s: device timeout\n", ifp->if_xname);
-	s = splnet();
-	vmxnet3_stop(ifp, 1);
-	vmxnet3_init(ifp);
-	splx(s);
-}
-
-void
-vmxnet3_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+vmxnet3_dma_free(struct vmxnet3_softc *sc, struct vmxnet3_dma_alloc *dma)
 {
-	struct vmxnet3_softc *sc = ifp->if_softc;
-
-	vmxnet3_link_state(sc);
-
-	ifmr->ifm_status = IFM_AVALID;
-	ifmr->ifm_active = IFM_ETHER;
-
-	if (ifp->if_link_state != LINK_STATE_UP)
-		return;
-
-	ifmr->ifm_status |= IFM_ACTIVE;
-
-	if (ifp->if_baudrate >= IF_Gbps(10ULL))
-		ifmr->ifm_active |= IFM_10G_T;
-}
+	bus_dma_tag_t t = sc->vmx_dmat;
 
-int
-vmxnet3_media_change(struct ifnet *ifp)
-{
-	return 0;
-}
+	bus_dmamap_unload(t, dma->dma_map);
+	bus_dmamap_destroy(t, dma->dma_map);
+	bus_dmamem_unmap(t, dma->dma_vaddr, dma->dma_size);
+	bus_dmamem_free(t, dma->dma_segs, 1);
 
-void *
-vmxnet3_dma_allocmem(struct vmxnet3_softc *sc, u_int size, u_int align, bus_addr_t *pa)
-{
-	bus_dma_tag_t t = sc->sc_dmat;
-	bus_dma_segment_t segs[1];
-	bus_dmamap_t map;
-	void *va;
-	int n;
-
-	if (bus_dmamem_alloc(t, size, align, 0, segs, 1, &n, BUS_DMA_NOWAIT))
-		return NULL;
-	if (bus_dmamem_map(t, segs, 1, size, &va, BUS_DMA_NOWAIT))
-		return NULL;
-	if (bus_dmamap_create(t, size, 1, size, 0, BUS_DMA_NOWAIT, &map))
-		return NULL;
-	if (bus_dmamap_load(t, map, va, size, NULL, BUS_DMA_NOWAIT))
-		return NULL;
-	memset(va, 0, size);
-	*pa = DMAADDR(map);
-	bus_dmamap_unload(t, map);
-	bus_dmamap_destroy(t, map);
-	return va;
+	memset(dma, 0, sizeof(*dma));
 }

Index: src/sys/arch/x86/pci/if_vmxreg.h
diff -u src/sys/arch/x86/pci/if_vmxreg.h:1.1 src/sys/arch/x86/pci/if_vmxreg.h:1.2
--- src/sys/arch/x86/pci/if_vmxreg.h:1.1	Tue Jun 10 01:42:39 2014
+++ src/sys/arch/x86/pci/if_vmxreg.h	Fri Nov 25 05:29:54 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vmxreg.h,v 1.1 2014/06/10 01:42:39 hikaru Exp $	*/
+/*	$NetBSD: if_vmxreg.h,v 1.2 2016/11/25 05:29:54 hikaru Exp $	*/
 /*	$OpenBSD: if_vmxreg.h,v 1.3 2013/08/28 10:19:19 reyk Exp $	*/
 
 /*
@@ -75,40 +75,35 @@ struct UPT1_RxStats {
 #define VMXNET3_CMD_RESET	0xcafe0002	/* reset device */
 #define VMXNET3_CMD_SET_RXMODE	0xcafe0003	/* set interface flags */
 #define VMXNET3_CMD_SET_FILTER	0xcafe0004	/* set address filter */
+#define VMXNET3_CMD_VLAN_FILTER	0xcafe0005	/* set VLAN filter */
 #define VMXNET3_CMD_GET_STATUS	0xf00d0000	/* get queue errors */
+#define VMXNET3_CMD_GET_STATS	0xf00d0001	/* get queue statistics */
 #define VMXNET3_CMD_GET_LINK	0xf00d0002	/* get link status */
 #define VMXNET3_CMD_GET_MACL	0xf00d0003
 #define VMXNET3_CMD_GET_MACH	0xf00d0004
+#define VMXNET3_CMD_GET_INTRCFG	0xf00d0008	/* get interrupt config */
 
 #define VMXNET3_DMADESC_ALIGN	128
+#define VMXNET3_INIT_GEN	1
 
 /* All descriptors are in little-endian format. */
 struct vmxnet3_txdesc {
-	uint64_t		tx_addr;
+	uint64_t	addr;
 
-	uint32_t		tx_word2;
-#define	VMXNET3_TX_LEN_M	0x00003fff
-#define	VMXNET3_TX_LEN_S	0
-#define VMXNET3_TX_GEN_M	0x00000001	/* generation */
-#define VMXNET3_TX_GEN_S	14
-#define VMXNET3_TX_RES0		0x00008000
-#define	VMXNET3_TX_DTYPE_M	0x00000001	/* descriptor type */
-#define	VMXNET3_TX_DTYPE_S	16		/* descriptor type */
-#define	VMXNET3_TX_RES1		0x00000002
-#define VMXNET3_TX_OP_M		0x00003fff	/* offloading position */
-#define	VMXNET3_TX_OP_S		18
-
-	uint32_t		tx_word3;
-#define VMXNET3_TX_HLEN_M	0x000003ff	/* header len */
-#define VMXNET3_TX_HLEN_S	0
-#define VMXNET3_TX_OM_M		0x00000003	/* offloading mode */
-#define VMXNET3_TX_OM_S		10
-#define VMXNET3_TX_EOP		0x00001000	/* end of packet */
-#define VMXNET3_TX_COMPREQ	0x00002000	/* completion request */
-#define VMXNET3_TX_RES2		0x00004000
-#define VMXNET3_TX_VTAG_MODE	0x00008000	/* VLAN tag insertion mode */
-#define VMXNET3_TX_VLANTAG_M	0x0000ffff
-#define VMXNET3_TX_VLANTAG_S	16
+	uint32_t	len:14;
+	uint32_t	gen:1;		/* Generation */
+	uint32_t	pad1:1;
+	uint32_t	dtype:1;	/* Descriptor type */
+	uint32_t	pad2:1;
+	uint32_t	offload_pos:14;	/* Offloading position */
+
+	uint32_t	hlen:10;	/* Header len */
+	uint32_t	offload_mode:2;	/* Offloading mode */
+	uint32_t	eop:1;		/* End of packet */
+	uint32_t	compreq:1;	/* Completion request */
+	uint32_t	pad3:1;
+	uint32_t	vtag_mode:1;	/* VLAN tag insertion mode */
+	uint32_t	vtag:16;	/* VLAN tag */
 } __packed;
 
 /* offloading modes */
@@ -117,39 +112,27 @@ struct vmxnet3_txdesc {
 #define VMXNET3_OM_TSO  3
 
 struct vmxnet3_txcompdesc {
-	uint32_t		txc_word0;	
-#define VMXNET3_TXC_EOPIDX_M	0x00000fff	/* eop index in Tx ring */
-#define VMXNET3_TXC_EOPIDX_S	0
-#define VMXNET3_TXC_RES0_M	0x000fffff
-#define VMXNET3_TXC_RES0_S	12
-
-	uint32_t		txc_word1;
-	uint32_t		txc_word2;
-
-	uint32_t		txc_word3;
-#define VMXNET3_TXC_RES2_M	0x00ffffff
-#define VMXNET3_TXC_TYPE_M	0x0000007f
-#define VMXNET3_TXC_TYPE_S	24
-#define VMXNET3_TXC_GEN_M	0x00000001
-#define VMXNET3_TXC_GEN_S	31
+	uint32_t	eop_idx:12;	/* EOP index in Tx ring */
+	uint32_t	pad1:20;
+
+	uint32_t	pad2:32;
+	uint32_t	pad3:32;
+
+	uint32_t	rsvd:24;
+	uint32_t	type:7;
+	uint32_t	gen:1;
 } __packed;
 
 struct vmxnet3_rxdesc {
-	uint64_t		rx_addr;
+	uint64_t	addr;
 
-	uint32_t		rx_word2;
-#define VMXNET3_RX_LEN_M	0x00003fff
-#define VMXNET3_RX_LEN_S	0
-#define VMXNET3_RX_BTYPE_M	0x00000001	/* buffer type */
-#define VMXNET3_RX_BTYPE_S	14
-#define VMXNET3_RX_DTYPE_M	0x00000001	/* descriptor type */
-#define VMXNET3_RX_DTYPE_S	15
-#define VMXNET3_RX_RES0_M	0x00007fff
-#define VMXNET3_RX_RES0_S	16
-#define VMXNET3_RX_GEN_M	0x00000001
-#define VMXNET3_RX_GEN_S	31
+	uint32_t	len:14;
+	uint32_t	btype:1;	/* Buffer type */
+	uint32_t	dtype:1;	/* Descriptor type */
+	uint32_t	rsvd:15;
+	uint32_t	gen:1;
 
-	uint32_t		rx_word3;
+	uint32_t	pad1:32;
 } __packed;
 
 /* buffer types */
@@ -157,48 +140,41 @@ struct vmxnet3_rxdesc {
 #define VMXNET3_BTYPE_BODY 1	/* body only */
 
 struct vmxnet3_rxcompdesc {
-	uint32_t		rxc_word0;
-#define VMXNET3_RXC_IDX_M	0x00000fff	/* Rx descriptor index */
-#define VMXNET3_RXC_IDX_S	0
-#define VMXNET3_RXC_RES0_M	0x00000003
-#define VMXNET3_RXC_RES0_S	12
-#define VMXNET3_RXC_EOP		0x00004000	/* end of packet */
-#define VMXNET3_RXC_SOP		0x00008000	/* start of packet */
-#define VMXNET3_RXC_QID_M	0x000003ff
-#define VMXNET3_RXC_QID_S	16
-#define VMXNET3_RXC_RSSTYPE_M	0x0000000f
-#define VMXNET3_RXC_RSSTYPE_S	26
-#define VMXNET3_RXC_NOCSUM	0x40000000	/* no checksum calculated */
-#define VMXNET3_RXC_RES1	0x80000000
-
-	uint32_t		rxc_word1;
-#define VMXNET3_RXC_RSSHASH_M	0xffffffff	/* RSS hash value */
-#define VMXNET3_RXC_RSSHASH_S	0
-
-	uint32_t		rxc_word2;
-#define VMXNET3_RXC_LEN_M	0x00003fff
-#define VMXNET3_RXC_LEN_S	0
-#define VMXNET3_RXC_ERROR	0x00004000
-#define VMXNET3_RXC_VLAN	0x00008000	/* 802.1Q VLAN frame */
-#define VMXNET3_RXC_VLANTAG_M	0x0000ffff	/* VLAN tag */
-#define VMXNET3_RXC_VLANTAG_S	16
-
-	uint32_t		rxc_word3;
-#define VMXNET3_RXC_CSUM_M	0x0000ffff	/* TCP/UDP checksum */
-#define VMXNET3_RXC_CSUM_S	16
-#define VMXNET3_RXC_CSUM_OK	0x00010000	/* TCP/UDP checksum ok */
-#define VMXNET3_RXC_UDP		0x00020000
-#define VMXNET3_RXC_TCP		0x00040000
-#define VMXNET3_RXC_IPSUM_OK	0x00080000	/* IP checksum ok */
-#define VMXNET3_RXC_IPV6	0x00100000
-#define VMXNET3_RXC_IPV4	0x00200000
-#define VMXNET3_RXC_FRAGMENT	0x00400000	/* IP fragment */
-#define VMXNET3_RXC_FCS		0x00800000	/* frame CRC correct */
-#define VMXNET3_RXC_TYPE_M	0x7f000000
-#define VMXNET3_RXC_GEN_M	0x00000001
-#define VMXNET3_RXC_GEN_S	31
+	uint32_t	rxd_idx:12;	/* Rx descriptor index */
+	uint32_t	pad1:2;
+	uint32_t	eop:1;		/* End of packet */
+	uint32_t	sop:1;		/* Start of packet */
+	uint32_t	qid:10;
+	uint32_t	rss_type:4;
+	uint32_t	no_csum:1;	/* No checksum calculated */
+	uint32_t	pad2:1;
+
+	uint32_t	rss_hash:32;	/* RSS hash value */
+
+	uint32_t	len:14;
+	uint32_t	error:1;
+	uint32_t	vlan:1;		/* 802.1Q VLAN frame */
+	uint32_t	vtag:16;	/* VLAN tag */
+
+	uint32_t	csum:16;
+	uint32_t	csum_ok:1;	/* TCP/UDP checksum ok */
+	uint32_t	udp:1;
+	uint32_t	tcp:1;
+	uint32_t	ipcsum_ok:1;	/* IP checksum OK */
+	uint32_t	ipv6:1;
+	uint32_t	ipv4:1;
+	uint32_t	fragment:1;	/* IP fragment */
+	uint32_t	fcs:1;		/* Frame CRC correct */
+	uint32_t	type:7;
+	uint32_t	gen:1;
 } __packed;
 
+#define VMXNET3_RCD_RSS_TYPE_NONE	0
+#define VMXNET3_RCD_RSS_TYPE_IPV4	1
+#define VMXNET3_RCD_RSS_TYPE_TCPIPV4	2
+#define VMXNET3_RCD_RSS_TYPE_IPV6	3
+#define VMXNET3_RCD_RSS_TYPE_TCPIPV6	4
+
 #define VMXNET3_REV1_MAGIC 0xbabefee1
 
 #define VMXNET3_GOS_UNKNOWN 0x00
@@ -214,7 +190,10 @@ struct vmxnet3_rxcompdesc {
 #define VMXNET3_MAX_TX_QUEUES 8
 #define VMXNET3_MAX_RX_QUEUES 16
 #define VMXNET3_MAX_INTRS (VMXNET3_MAX_TX_QUEUES + VMXNET3_MAX_RX_QUEUES + 1)
-#define VMXNET3_NINTR 1
+
+#define VMXNET3_RX_INTR_INDEX 0
+#define VMXNET3_TX_INTR_INDEX 1
+#define VMXNET3_EV_INTR_INDEX 2
 
 #define VMXNET3_ICTRL_DISABLE_ALL 0x01
 
@@ -233,6 +212,15 @@ struct vmxnet3_rxcompdesc {
 #define VMXNET3_MAX_MTU 9000
 #define VMXNET3_MIN_MTU 60
 
+#define VMXNET3_IMM_AUTO	0x00
+#define VMXNET3_IMM_ACTIVE	0x01
+#define VMXNET3_IMM_LAZY	0x02
+
+#define VMXNET3_IT_AUTO   0x00
+#define VMXNET3_IT_LEGACY 0x01
+#define VMXNET3_IT_MSI    0x02
+#define VMXNET3_IT_MSIX   0x03
+
 struct vmxnet3_driver_shared {
 	uint32_t magic;
 	uint32_t pad1;
@@ -326,3 +314,25 @@ struct vmxnet3_rxq_shared {
 
 	uint8_t pad4[88];
 } __packed;
+
+#define UPT1_RSS_HASH_TYPE_NONE		0x00
+#define UPT1_RSS_HASH_TYPE_IPV4		0x01
+#define UPT1_RSS_HASH_TYPE_TCP_IPV4	0x02
+#define UPT1_RSS_HASH_TYPE_IPV6		0x04
+#define UPT1_RSS_HASH_TYPE_TCP_IPV6	0x08
+
+#define UPT1_RSS_HASH_FUNC_NONE		0x00
+#define UPT1_RSS_HASH_FUNC_TOEPLITZ	0x01
+
+#define UPT1_RSS_MAX_KEY_SIZE		40
+#define UPT1_RSS_MAX_IND_TABLE_SIZE	128
+
+struct vmxnet3_rss_shared {
+	uint16_t		hash_type;
+	uint16_t		hash_func;
+	uint16_t		hash_key_size;
+	uint16_t		ind_table_size;
+	uint8_t			hash_key[UPT1_RSS_MAX_KEY_SIZE];
+	uint8_t			ind_table[UPT1_RSS_MAX_IND_TABLE_SIZE];
+} __packed;
+

Reply via email to