git: 0acab8b3d133 - main - enic(4): fix down/up, MTU changes and more

From: Doug Ambrisko <ambrisko_at_FreeBSD.org>
Date: Thu, 09 Jan 2025 17:06:02 UTC
The branch main has been updated by ambrisko:

URL: https://cgit.FreeBSD.org/src/commit/?id=0acab8b3d1336d4db73a9946ef76b4bcd0b0aabe

commit 0acab8b3d1336d4db73a9946ef76b4bcd0b0aabe
Author:     Doug Ambrisko <ambrisko@FreeBSD.org>
AuthorDate: 2025-01-09 16:28:37 +0000
Commit:     Doug Ambrisko <ambrisko@FreeBSD.org>
CommitDate: 2025-01-09 16:52:54 +0000

    enic(4): fix down/up, MTU changes and more
    
    ifconfig down/up cycles was not working.  Fix that which is required
    to support MTU changes.  Now doing ifconfig enic0 mtu 3000 for example
    works.  If the MTU is changes in the VIC HW configuration, that is not
    reflected in and the OS reports the default 1500.  I need to look at
    that but changing it via ifconfig works.  So this is different then
    what Linux does.
    
    Change TX interrupt allocation to be in this driver.  Change the admin
    interrupt count to 2.  This make multiple queues work but need to be
    done as pairs so if the VIC has more TX or RX queues setup in the
    VIC configuration it will use the lesser value.
    
    While updating the TX interrupt also add support for devcmd2.
    
    Enable checksum offloading.
    
    PR:     282095
---
 sys/dev/enic/cq_desc.h       |  15 ---
 sys/dev/enic/enic.h          |  76 ++++++--------
 sys/dev/enic/enic_res.c      |   4 +-
 sys/dev/enic/enic_res.h      |   2 -
 sys/dev/enic/enic_txrx.c     |  39 +++++--
 sys/dev/enic/if_enic.c       | 173 +++++++++++++++++++++++++++----
 sys/dev/enic/vnic_cq.h       |   5 +-
 sys/dev/enic/vnic_dev.c      | 235 +++++++++++++++++++++++++++++++++++++------
 sys/dev/enic/vnic_dev.h      |   8 +-
 sys/dev/enic/vnic_intr.c     |   2 +-
 sys/dev/enic/vnic_intr.h     |   2 +-
 sys/dev/enic/vnic_resource.h |   1 +
 sys/dev/enic/vnic_rq.c       |   5 +-
 sys/dev/enic/vnic_rq.h       |   1 -
 sys/dev/enic/vnic_rss.h      |   5 -
 sys/dev/enic/vnic_wq.c       | 104 ++++++++++++++++++-
 sys/dev/enic/vnic_wq.h       |  18 +++-
 17 files changed, 559 insertions(+), 136 deletions(-)

diff --git a/sys/dev/enic/cq_desc.h b/sys/dev/enic/cq_desc.h
index ae8847c6d9a1..4fb8cce7212e 100644
--- a/sys/dev/enic/cq_desc.h
+++ b/sys/dev/enic/cq_desc.h
@@ -44,14 +44,6 @@ struct cq_desc {
 #define CQ_DESC_COMP_NDX_BITS    12
 #define CQ_DESC_COMP_NDX_MASK    ((1 << CQ_DESC_COMP_NDX_BITS) - 1)
 
-static inline void cq_color_enc(struct cq_desc *desc, const u8 color)
-{
-	if (color)
-		desc->type_color |=  (1 << CQ_DESC_COLOR_SHIFT);
-	else
-		desc->type_color &= ~(1 << CQ_DESC_COLOR_SHIFT);
-}
-
 static inline void cq_desc_enc(struct cq_desc *desc,
 	const u8 type, const u8 color, const u16 q_number,
 	const u16 completed_index)
@@ -87,11 +79,4 @@ static inline void cq_desc_dec(const struct cq_desc *desc_arg,
 		CQ_DESC_COMP_NDX_MASK;
 }
 
-static inline void cq_color_dec(const struct cq_desc *desc_arg, u8 *color)
-{
-	volatile const struct cq_desc *desc = desc_arg;
-
-	*color = (desc->type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
-}
-
 #endif /* _CQ_DESC_H_ */
diff --git a/sys/dev/enic/enic.h b/sys/dev/enic/enic.h
index 8c2212726548..eec6de823c9d 100644
--- a/sys/dev/enic/enic.h
+++ b/sys/dev/enic/enic.h
@@ -108,13 +108,13 @@ struct vnic_res {
 #define ENIC_DEFAULT_VXLAN_PORT		4789
 
 /*
- * Interrupt 0: LSC and errors
  * Interrupt 1: rx queue 0
  * Interrupt 2: rx queue 1
  * ...
+ * Interrupt x: LSC and errors
  */
 #define ENICPMD_LSC_INTR_OFFSET 0
-#define ENICPMD_RXQ_INTR_OFFSET 1
+#define ENICPMD_RXQ_INTR_OFFSET 0
 
 #include "vnic_devcmd.h"
 
@@ -152,6 +152,9 @@ struct vnic_dev {
 	u64 args[VNIC_DEVCMD_NARGS];
 	int in_reset;
 	struct vnic_intr_coal_timer_info intr_coal_timer_info;
+	struct devcmd2_controller *devcmd2;
+	int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+	    int wait);
 	void *(*alloc_consistent)(void *priv, size_t size,
 	    bus_addr_t *dma_handle, struct iflib_dma_info *res, u8 *name);
 	void (*free_consistent)(void *priv, size_t size, void *vaddr,
@@ -175,6 +178,28 @@ struct intr_queue {
 	struct enic_softc *softc;
 };
 
+#define ENIC_MAX_LINK_SPEEDS		3
+#define ENIC_LINK_SPEED_10G		10000
+#define ENIC_LINK_SPEED_4G		4000
+#define ENIC_LINK_40G_INDEX		2
+#define ENIC_LINK_10G_INDEX		1
+#define ENIC_LINK_4G_INDEX		0
+#define ENIC_RX_COALESCE_RANGE_END	125
+#define ENIC_AIC_TS_BREAK		100
+
+struct enic_rx_coal {
+	u32 small_pkt_range_start;
+	u32 large_pkt_range_start;
+	u32 range_end;
+	u32 use_adaptive_rx_coalesce;
+};
+
+/* Store only the lower range.  Higher range is given by fw. */
+struct enic_intr_mod_range {
+	u32 small_pkt_range_start;
+	u32 large_pkt_range_start;
+};
+
 struct enic {
 	struct enic *next;
 	struct rte_pci_device *pdev;
@@ -267,6 +292,9 @@ struct enic {
 	uint64_t tx_offload_mask; /* PKT_TX flags accepted */
 	struct enic_softc *softc;
 	int port_mtu;
+	struct enic_rx_coal rx_coalesce_setting;
+	u32 rx_coalesce_usecs;
+	u32 tx_coalesce_usecs;
 };
 
 struct enic_softc {
@@ -307,11 +335,6 @@ struct enic_softc {
 
 /* Per-instance private data structure */
 
-static inline unsigned int enic_vnic_rq_count(struct enic *enic)
-{
-	return enic->rq_count;
-}
-
 static inline unsigned int enic_cq_rq(struct enic *enic, unsigned int rq)
 {
 	return rq;
@@ -322,21 +345,6 @@ static inline unsigned int enic_cq_wq(struct enic *enic, unsigned int wq)
 	return enic->rq_count + wq;
 }
 
-static inline uint32_t
-enic_ring_add(uint32_t n_descriptors, uint32_t i0, uint32_t i1)
-{
-	uint32_t d = i0 + i1;
-	d -= (d >= n_descriptors) ? n_descriptors : 0;
-	return d;
-}
-
-static inline uint32_t
-enic_ring_sub(uint32_t n_descriptors, uint32_t i0, uint32_t i1)
-{
-	int32_t d = i1 - i0;
-	return (uint32_t)((d < 0) ? ((int32_t)n_descriptors + d) : d);
-}
-
 static inline uint32_t
 enic_ring_incr(uint32_t n_descriptors, uint32_t idx)
 {
@@ -346,34 +354,14 @@ enic_ring_incr(uint32_t n_descriptors, uint32_t idx)
 	return idx;
 }
 
-void enic_free_wq(void *txq);
-int enic_alloc_intr_resources(struct enic *enic);
 int enic_setup_finish(struct enic *enic);
-int enic_alloc_wq(struct enic *enic, uint16_t queue_idx,
-		  unsigned int socket_id, uint16_t nb_desc);
 void enic_start_wq(struct enic *enic, uint16_t queue_idx);
 int enic_stop_wq(struct enic *enic, uint16_t queue_idx);
 void enic_start_rq(struct enic *enic, uint16_t queue_idx);
-void enic_free_rq(void *rxq);
-int enic_set_vnic_res(struct enic *enic);
-int enic_init_rss_nic_cfg(struct enic *enic);
-int enic_set_rss_reta(struct enic *enic, union vnic_rss_cpu *rss_cpu);
-int enic_set_vlan_strip(struct enic *enic);
+int enic_stop_rq(struct enic *enic, uint16_t queue_idx);
+void enic_dev_disable(struct enic *enic);
 int enic_enable(struct enic *enic);
 int enic_disable(struct enic *enic);
-void enic_remove(struct enic *enic);
-int enic_get_link_status(struct enic *enic);
-void enic_dev_stats_clear(struct enic *enic);
-void enic_add_packet_filter(struct enic *enic);
-int enic_set_mac_address(struct enic *enic, uint8_t *mac_addr);
-int enic_del_mac_address(struct enic *enic, int mac_index);
-unsigned int enic_cleanup_wq(struct enic *enic, struct vnic_wq *wq);
-
-void enic_post_wq_index(struct vnic_wq *wq);
-int enic_probe(struct enic *enic);
-int enic_clsf_init(struct enic *enic);
-void enic_clsf_destroy(struct enic *enic);
-int enic_set_mtu(struct enic *enic, uint16_t new_mtu);
 int enic_link_update(struct enic *enic);
 bool enic_use_vector_rx_handler(struct enic *enic);
 void enic_fdir_info(struct enic *enic);
diff --git a/sys/dev/enic/enic_res.c b/sys/dev/enic/enic_res.c
index d264874557a0..413873ad0fb4 100644
--- a/sys/dev/enic/enic_res.c
+++ b/sys/dev/enic/enic_res.c
@@ -95,11 +95,11 @@ int enic_get_vnic_config(struct enic *enic)
 
 	dev_info(enic_get_dev(enic),
 		"vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
-		"wq/rq %d/%d mtu d, max mtu:%d\n",
+		"wq/rq %d/%d mtu %d, max mtu:%d\n",
 		enic->mac_addr[0], enic->mac_addr[1], enic->mac_addr[2],
 		enic->mac_addr[3], enic->mac_addr[4], enic->mac_addr[5],
 		c->wq_desc_count, c->rq_desc_count,
-		 /* enic->rte_dev->data->mtu, */ enic->max_mtu);
+		c->mtu, enic->max_mtu);
 	dev_info(enic_get_dev(enic), "vNIC csum tx/rx %s/%s "
 		"rss %s intr mode %s type %s timer %d usec "
 		"loopback tag 0x%04x\n",
diff --git a/sys/dev/enic/enic_res.h b/sys/dev/enic/enic_res.h
index 1a6f3a3ca98f..82963e61a44f 100644
--- a/sys/dev/enic/enic_res.h
+++ b/sys/dev/enic/enic_res.h
@@ -67,7 +67,5 @@ int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type,
 	u8 ig_vlan_strip_en);
 void enic_get_res_counts(struct enic *enic);
 void enic_init_vnic_resources(struct enic *enic);
-int enic_alloc_vnic_resources(struct enic *);
-void enic_free_vnic_resources(struct enic *);
 
 #endif /* _ENIC_RES_H_ */
diff --git a/sys/dev/enic/enic_txrx.c b/sys/dev/enic/enic_txrx.c
index 5a557fc7f94a..169041587d06 100644
--- a/sys/dev/enic/enic_txrx.c
+++ b/sys/dev/enic/enic_txrx.c
@@ -103,6 +103,7 @@ enic_isc_txd_encap(void *vsc, if_pkt_info_t pi)
 
 	softc = vsc;
 	enic = &softc->enic;
+	if_softc_ctx_t scctx = softc->scctx;
 
 	wq = &enic->wq[pi->ipi_qsidx];
 	nsegs = pi->ipi_nsegs;
@@ -112,6 +113,9 @@ enic_isc_txd_encap(void *vsc, if_pkt_info_t pi)
 	head_idx = wq->head_idx;
 	desc_count = wq->ring.desc_count;
 
+	if ((scctx->isc_capenable & IFCAP_RXCSUM) != 0)
+		offload_mode |= WQ_ENET_OFFLOAD_MODE_CSUM;
+
 	for (i = 0; i < nsegs; i++) {
 		eop = 0;
 		cq = 0;
@@ -320,7 +324,7 @@ enic_isc_rxd_flush(void *vsc, uint16_t rxqid, uint8_t flid, qidx_t pidx)
 static int
 enic_legacy_intr(void *xsc)
 {
-	return -1;
+	return (1);
 }
 
 static inline void
@@ -375,7 +379,7 @@ enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, u8 type,
 
 	vnic_wq_service(&enic->wq[q_number], cq_desc,
 			completed_index, NULL, opaque);
-	return 0;
+	return (0);
 }
 
 static void
@@ -384,7 +388,7 @@ vnic_rq_service(struct vnic_rq *rq, struct cq_desc *cq_desc,
     void(*buf_service)(struct vnic_rq *rq, struct cq_desc *cq_desc,
     /* struct vnic_rq_buf * *buf, */ int skipped, void *opaque), void *opaque)
 {
-
+	if_softc_ctx_t scctx;
 	if_rxd_info_t ri = (if_rxd_info_t) opaque;
 	u8 type, color, eop, sop, ingress_port, vlan_stripped;
 	u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof;
@@ -396,6 +400,8 @@ vnic_rq_service(struct vnic_rq *rq, struct cq_desc *cq_desc,
 	int cqidx;
 	if_rxd_frag_t frag;
 
+	scctx = rq->vdev->softc->scctx;
+
 	cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
 	    &type, &color, &q_number, &completed_index,
 	    &ingress_port, &fcoe, &eop, &sop, &rss_type,
@@ -419,6 +425,11 @@ vnic_rq_service(struct vnic_rq *rq, struct cq_desc *cq_desc,
 	ri->iri_cidx = cqidx;
 	ri->iri_nfrags = 1;
 	ri->iri_len = bytes_written;
+
+	if ((scctx->isc_capenable & IFCAP_RXCSUM) != 0)
+		if (!csum_not_calc && (tcp_udp_csum_ok || ipv4_csum_ok)) {
+			ri->iri_csum_flags = (CSUM_IP_CHECKED | CSUM_IP_VALID);
+		}
 }
 
 static int
@@ -431,7 +442,7 @@ enic_rq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc,
 	vnic_rq_service(&enic->rq[ri->iri_qsidx], cq_desc, completed_index,
 	    VNIC_RQ_RETURN_DESC, NULL, /* enic_rq_indicate_buf, */ opaque);
 
-	return 0;
+	return (0);
 }
 
 void
@@ -468,10 +479,8 @@ enic_stop_wq(struct enic *enic, uint16_t queue_idx)
 	int ret;
 
 	ret = vnic_wq_disable(&enic->wq[queue_idx]);
-	if (ret)
-		return ret;
 
-	return 0;
+	return (ret);
 }
 
 void
@@ -483,3 +492,19 @@ enic_start_rq(struct enic *enic, uint16_t queue_idx)
 	vnic_rq_enable(rq);
 	enic_initial_post_rx(enic, rq);
 }
+
+int
+enic_stop_rq(struct enic *enic, uint16_t queue_idx)
+{
+	int ret;
+
+	ret = vnic_rq_disable(&enic->rq[queue_idx]);
+
+	return (ret);
+}
+
+
+void
+enic_dev_disable(struct enic *enic) {
+	vnic_dev_disable(enic->vdev);
+}
diff --git a/sys/dev/enic/if_enic.c b/sys/dev/enic/if_enic.c
index dc0c0d028e20..26776244778e 100644
--- a/sys/dev/enic/if_enic.c
+++ b/sys/dev/enic/if_enic.c
@@ -201,11 +201,11 @@ static struct if_shared_ctx enic_sctx_init = {
 							 * descriptor */
 	.isc_rx_nsegments = 1,	/* One mapping per descriptor */
 	.isc_rx_maxsegsize = ENIC_DEFAULT_RX_MAX_PKT_SIZE,
-	.isc_admin_intrcnt = 3,
+	.isc_admin_intrcnt = 2,
 	.isc_vendor_info = enic_vendor_info_array,
 	.isc_driver_version = "1",
 	.isc_driver = &enic_iflib_driver,
-	.isc_flags = IFLIB_HAS_RXCQ | IFLIB_HAS_TXCQ,
+	.isc_flags = IFLIB_HAS_RXCQ | IFLIB_HAS_TXCQ | IFLIB_SKIP_MSIX,
 
 	/*
 	 * Number of receive queues per receive queue set, with associated
@@ -235,6 +235,99 @@ enic_register(device_t dev)
 	return (&enic_sctx_init);
 }
 
+static int
+enic_allocate_msix(struct enic_softc *softc) {
+	if_ctx_t ctx;
+	if_softc_ctx_t scctx;
+	if_shared_ctx_t sctx;
+	device_t dev;
+	cpuset_t cpus;
+	int queues, vectors, requested;
+	int err = 0;
+
+	dev = softc->dev;
+	ctx = softc->ctx;
+	scctx = softc->scctx;
+	sctx = iflib_get_sctx(ctx);
+
+	if (bus_get_cpus(dev, INTR_CPUS, sizeof(cpus), &cpus) != 0) {
+		device_printf(dev, "Unable to fetch CPU list\n");
+		CPU_COPY(&all_cpus, &cpus);
+	}
+
+
+	queues = CPU_COUNT(&cpus);
+	queues = imin(queues, scctx->isc_nrxqsets);
+	queues = imin(queues, scctx->isc_ntxqsets);
+	requested = queues * 2 + sctx->isc_admin_intrcnt;
+	scctx->isc_nrxqsets = queues;
+	scctx->isc_ntxqsets = queues;
+
+	vectors = requested;
+	if ((err = pci_alloc_msix(dev, &vectors)) != 0) {
+		device_printf(dev,
+                    "failed to allocate %d MSI-X vectors, err: %d\n", requested,
+                    err);
+		err = 1;
+		goto enic_allocate_msix_out;
+	} else {
+		if (vectors != requested) {
+			device_printf(dev,
+			    "Unable to allocate sufficient MSI-X vectors "
+			     "(got %d, need %d)\n", requested, vectors);
+			pci_release_msi(dev);
+			err = 1;
+			goto enic_allocate_msix_out;
+		}
+	}
+
+	device_printf(dev, "Using MSI-X interrupts with %d vectors\n",
+	    vectors);
+
+	scctx->isc_intr = IFLIB_INTR_MSIX;
+	scctx->isc_vectors = vectors;
+
+enic_allocate_msix_out:
+	return (err);
+
+}
+
+static struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = {
+	{0,  0}, /* 0  - 4  Gbps */
+	{0,  3}, /* 4  - 10 Gbps */
+	{3,  6}, /* 10 - 40 Gbps */
+};
+
+static void enic_set_rx_coal_setting(struct enic *enic)
+{
+	unsigned int speed;
+	int index = -1;
+	struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting;
+
+	/* 1. Read the link speed from fw
+	 * 2. Pick the default range for the speed
+	 * 3. Update it in enic->rx_coalesce_setting
+	 */
+	speed = vnic_dev_port_speed(enic->vdev);
+	if (ENIC_LINK_SPEED_10G < speed)
+		index = ENIC_LINK_40G_INDEX;
+	else if (ENIC_LINK_SPEED_4G < speed)
+		index = ENIC_LINK_10G_INDEX;
+	else
+		index = ENIC_LINK_4G_INDEX;
+
+	rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start;
+	rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start;
+	rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END;
+
+	/* Start with the value provided by UCSM */
+	for (index = 0; index < enic->rq_count; index++)
+		enic->cq[index].cur_rx_coal_timeval =
+		enic->config.intr_timer_usec;
+
+	rx_coal->use_adaptive_rx_coalesce = 1;
+}
+
 static int
 enic_attach_pre(if_ctx_t ctx)
 {
@@ -283,6 +376,8 @@ enic_attach_pre(if_ctx_t ctx)
 	ENIC_LOCK(softc);
 	vnic_dev_register(vdev, &softc->mem, 1);
 	enic->vdev = vdev;
+	vnic_dev_cmd_init(enic->vdev);
+
 	vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
 
 	vnic_dev_cmd(vdev, CMD_INIT_v1, &a0, &a1, wait);
@@ -326,6 +421,7 @@ enic_attach_pre(if_ctx_t ctx)
 
 	/* Set ingress vlan rewrite mode before vnic initialization */
 	enic->ig_vlan_rewrite_mode = IG_VLAN_REWRITE_MODE_UNTAG_DEFAULT_VLAN;
+	enic->ig_vlan_rewrite_mode = IG_VLAN_REWRITE_MODE_PRIORITY_TAG_DEFAULT_VLAN;
 	err = vnic_dev_set_ig_vlan_rewrite_mode(enic->vdev,
 						enic->ig_vlan_rewrite_mode);
 	if (err) {
@@ -360,8 +456,10 @@ enic_attach_pre(if_ctx_t ctx)
 	softc->scctx = iflib_get_softc_ctx(ctx);
 	scctx = softc->scctx;
 	scctx->isc_txrx = &enic_txrx;
-	scctx->isc_capabilities = scctx->isc_capenable = 0;
+	scctx->isc_capabilities = scctx->isc_capenable = \
+		IFCAP_HWCSUM;
 	scctx->isc_tx_csum_flags = 0;
+	if_setmtu(softc->ifp, enic->config.mtu);
 	scctx->isc_max_frame_size = enic->config.mtu + ETHER_HDR_LEN + \
 		ETHER_CRC_LEN;
 	scctx->isc_nrxqsets_max = enic->conf_rq_count;
@@ -389,7 +487,6 @@ enic_attach_pre(if_ctx_t ctx)
 	}
 	scctx->isc_tx_nsegments = 31;
 
-	scctx->isc_vectors = enic->conf_cq_count;
 	scctx->isc_msix_bar = -1;
 
 	ifmedia_add(softc->media, IFM_ETHER | IFM_AUTO, 0, NULL);
@@ -416,12 +513,20 @@ enic_attach_pre(if_ctx_t ctx)
 	err = vnic_dev_alloc_stats_mem(enic->vdev);
 	if (err) {
 		dev_err(enic, "Failed to allocate cmd memory, aborting\n");
+		goto err_out_dev_close;
+	}
+
+        err = enic_allocate_msix(softc);
+        if (err) {
+		dev_err(enic, "Failed to allocate MSIX, aborting\n");
+		goto err_out_dev_close;
 	}
 
 	return (rc);
 
 err_out_dev_close:
 	vnic_dev_close(enic->vdev);
+	vnic_dev_deinit_devcmd2(enic->vdev);
 err_out_unregister:
 	free(softc->vdev.devcmd, M_DEVBUF);
 	free(softc->enic.intr_queues, M_DEVBUF);
@@ -482,9 +587,10 @@ enic_msix_intr_assign(if_ctx_t ctx, int msix)
 		snprintf(irq_name, sizeof(irq_name), "etxq%d:%d", i -
 		    scctx->isc_nrxqsets, device_get_unit(softc->dev));
 
-
-		iflib_softirq_alloc_generic(ctx, &enic->intr_queues[i].intr_irq, IFLIB_INTR_TX, &enic->wq[i - scctx->isc_nrxqsets], i - scctx->isc_nrxqsets, irq_name);
-
+		iflib_softirq_alloc_generic(ctx,
+		    &enic->intr_queues[i].intr_irq, IFLIB_INTR_TX,
+		    &enic->wq[i - scctx->isc_nrxqsets], i - scctx->isc_nrxqsets,
+		    irq_name);
 
 		enic->intr[i].index = i;
 		enic->intr[i].vdev = enic->vdev;
@@ -567,6 +673,7 @@ enic_attach_post(if_ctx_t ctx)
 	enic_setup_sysctl(softc);
 
 	enic_init_vnic_resources(enic);
+	enic_set_rx_coal_setting(enic);
 	enic_setup_finish(enic);
 
 	ifmedia_add(softc->media, IFM_ETHER | IFM_AUTO, 0, NULL);
@@ -589,7 +696,9 @@ enic_detach(if_ctx_t ctx)
 	enic_free_irqs(softc);
 
 	ENIC_LOCK(softc);
+	vnic_dev_deinit(enic->vdev);
 	vnic_dev_close(enic->vdev);
+	vnic_dev_deinit_devcmd2(enic->vdev);
 	free(softc->vdev.devcmd, M_DEVBUF);
 	pci_disable_busmaster(softc->dev);
 	enic_pci_mapping_free(softc);
@@ -807,6 +916,11 @@ enic_stop(if_ctx_t ctx)
 	struct enic    *enic;
 	if_softc_ctx_t	scctx;
 	unsigned int	index;
+	struct vnic_wq *wq;
+	struct vnic_rq *rq;
+	struct vnic_cq *cq;
+	unsigned int	cq_wq, cq_rq;
+
 
 	softc = iflib_get_softc(ctx);
 	scctx = softc->scctx;
@@ -817,15 +931,36 @@ enic_stop(if_ctx_t ctx)
 	softc->link_active = 0;
 	softc->stopped = 1;
 
+	enic_dev_disable(enic);
+
 	for (index = 0; index < scctx->isc_ntxqsets; index++) {
 		enic_stop_wq(enic, index);
 		vnic_wq_clean(&enic->wq[index]);
 		vnic_cq_clean(&enic->cq[enic_cq_rq(enic, index)]);
+
+		wq = &softc->enic.wq[index];
+		wq->ring.desc_avail = wq->ring.desc_count - 1;
+		wq->ring.last_count = wq->ring.desc_count;
+		wq->head_idx = 0;
+		wq->tail_idx = 0;
+
+		cq_wq = enic_cq_wq(&softc->enic, index);
+		cq = &softc->enic.cq[cq_wq];
+		cq->ring.desc_avail = cq->ring.desc_count - 1;
 	}
 
 	for (index = 0; index < scctx->isc_nrxqsets; index++) {
+		enic_stop_rq(enic, index);
 		vnic_rq_clean(&enic->rq[index]);
 		vnic_cq_clean(&enic->cq[enic_cq_wq(enic, index)]);
+
+		rq = &softc->enic.rq[index];
+		cq_rq = enic_cq_rq(&softc->enic, index);
+		cq = &softc->enic.cq[cq_rq];
+
+		cq->ring.desc_avail = cq->ring.desc_count - 1;
+		rq->ring.desc_avail = rq->ring.desc_count - 1;
+		rq->need_initial_post = true;
 	}
 
 	for (index = 0; index < scctx->isc_vectors; index++) {
@@ -845,6 +980,9 @@ enic_init(if_ctx_t ctx)
 	scctx = softc->scctx;
 	enic = &softc->enic;
 
+
+	enic_init_vnic_resources(enic);
+
 	for (index = 0; index < scctx->isc_ntxqsets; index++)
 		enic_prep_wq_for_simple_tx(&softc->enic, index);
 
@@ -862,6 +1000,8 @@ enic_init(if_ctx_t ctx)
 	vnic_dev_enable_wait(enic->vdev);
 	ENIC_UNLOCK(softc);
 
+	softc->stopped = 0;
+
 	enic_link_status(softc);
 }
 
@@ -942,12 +1082,14 @@ enic_mtu_set(if_ctx_t ctx, uint32_t mtu)
 	softc = iflib_get_softc(ctx);
 	enic = &softc->enic;
 
+	enic_stop(softc->ctx);
 	if (mtu > enic->port_mtu){
 		return (EINVAL);
 	}
 
 	enic->config.mtu = mtu;
 	scctx->isc_max_frame_size = mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
+	enic_init(softc->ctx);
 
 	return (0);
 }
@@ -1026,7 +1168,6 @@ static void
 enic_update_admin_status(if_ctx_t ctx)
 {
 	struct enic_softc *softc;
-
 	softc = iflib_get_softc(ctx);
 
 	enic_link_status(softc);
@@ -1357,7 +1498,7 @@ enic_dev_init(struct enic *enic)
 		if (vnic_dev_overlay_offload_cfg(enic->vdev,
 		   OVERLAY_CFG_VXLAN_PORT_UPDATE, ENIC_DEFAULT_VXLAN_PORT)) {
 			dev_err(enic, "failed to update vxlan port\n");
-			return -EINVAL;
+			return (EINVAL);
 		}
 	}
 	return 0;
@@ -1441,7 +1582,7 @@ enic_dev_wait(struct vnic_dev *vdev, int (*start) (struct vnic_dev *, int),
 			return 0;
 		usleep(1000);
 	}
-	return -ETIMEDOUT;
+	return (ETIMEDOUT);
 }
 
 static int
@@ -1452,7 +1593,7 @@ enic_map_bar(struct enic_softc *softc, struct enic_bar_info *bar, int bar_num,
 
 	if (bar->res != NULL) {
 		device_printf(softc->dev, "Bar %d already mapped\n", bar_num);
-		return EDOOFUS;
+		return (EDOOFUS);
 	}
 
 	bar->rid = PCIR_BAR(bar_num);
@@ -1481,20 +1622,18 @@ enic_init_vnic_resources(struct enic *enic)
 	unsigned int rxq_interrupt_enable = 0;
 	unsigned int rxq_interrupt_offset = ENICPMD_RXQ_INTR_OFFSET;
 	unsigned int txq_interrupt_enable = 0;
-	unsigned int txq_interrupt_offset = ENICPMD_RXQ_INTR_OFFSET;
+	unsigned int txq_interrupt_offset;
 	unsigned int index = 0;
 	unsigned int cq_idx;
 	if_softc_ctx_t scctx;
 
 	scctx = enic->softc->scctx;
 
-
 	rxq_interrupt_enable = 1;
-	txq_interrupt_enable = 1;
+	txq_interrupt_enable = 0;
 
 	rxq_interrupt_offset = 0;
-	txq_interrupt_offset = enic->intr_count - 2;
-	txq_interrupt_offset = 1;
+	txq_interrupt_offset = scctx->isc_nrxqsets;
 
 	for (index = 0; index < enic->intr_count; index++) {
 		vnic_intr_alloc(enic->vdev, &enic->intr[index], index);
@@ -1568,7 +1707,7 @@ enic_update_packet_filter(struct enic *enic)
 }
 
 static bool
-enic_if_needs_restart(if_ctx_t ctx __unused, enum iflib_restart_event event)
+enic_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event)
 {
 	switch (event) {
 	case IFLIB_RESTART_VLAN_CONFIG:
diff --git a/sys/dev/enic/vnic_cq.h b/sys/dev/enic/vnic_cq.h
index 26f9009612c5..b4549ee58c64 100644
--- a/sys/dev/enic/vnic_cq.h
+++ b/sys/dev/enic/vnic_cq.h
@@ -63,6 +63,8 @@ struct vnic_cq {
 	unsigned int to_clean;
 	unsigned int last_color;
 	unsigned int interrupt_offset;
+	unsigned int cur_rx_coal_timeval;
+	unsigned int tobe_rx_coal_timeval;
 #ifdef ENIC_AIC
 	struct vnic_rx_bytes_counter pkt_size_counter;
 	unsigned int cur_rx_coal_timeval;
@@ -75,15 +77,12 @@ struct vnic_cq {
 	int nrxqsets_start;
 };
 
-void vnic_cq_free(struct vnic_cq *cq);
 void vnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
     unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
     unsigned int cq_tail_color, unsigned int interrupt_enable,
     unsigned int cq_entry_enable, unsigned int message_enable,
     unsigned int interrupt_offset, u64 message_addr);
 void vnic_cq_clean(struct vnic_cq *cq);
-int vnic_cq_mem_size(struct vnic_cq *cq, unsigned int desc_count,
-    unsigned int desc_size);
 
 static inline unsigned int vnic_cq_service(struct vnic_cq *cq,
     unsigned int work_to_do,
diff --git a/sys/dev/enic/vnic_dev.c b/sys/dev/enic/vnic_dev.c
index 3425d7372e56..2d555cb2b34d 100644
--- a/sys/dev/enic/vnic_dev.c
+++ b/sys/dev/enic/vnic_dev.c
@@ -44,7 +44,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
 	u8 type;
 
 	if (num_bars == 0)
-		return -EINVAL;
+		return (EINVAL);
 
 	rh = malloc(sizeof(*rh), M_DEVBUF, M_NOWAIT | M_ZERO);
 	mrh = malloc(sizeof(*mrh), M_DEVBUF, M_NOWAIT | M_ZERO);
@@ -52,7 +52,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
 		pr_err("vNIC BAR0 res hdr not mem-mapped\n");
 		free(rh, M_DEVBUF);
 		free(mrh, M_DEVBUF);
-		return -EINVAL;
+		return (EINVAL);
 	}
 
 	/* Check for mgmt vnic in addition to normal vnic */
@@ -69,7 +69,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
 				rh->magic, rh->version);
 			free(rh, M_DEVBUF);
 			free(mrh, M_DEVBUF);
-			return -EINVAL;
+			return (EINVAL);
 		}
 	}
 
@@ -97,6 +97,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
 		case RES_TYPE_INTR_CTRL:
 		case RES_TYPE_INTR_PBA_LEGACY:
 		case RES_TYPE_DEVCMD:
+		case RES_TYPE_DEVCMD2:
 			break;
 		default:
 			ENIC_BUS_READ_REGION_4(softc, mem, r_offset, (void *)r, sizeof(*r) / 4);
@@ -189,12 +190,12 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 	status = ENIC_BUS_READ_4(devcmd, DEVCMD_STATUS);
 	if (status == 0xFFFFFFFF) {
 		/* PCI-e target device is gone */
-		return -ENODEV;
+		return (ENODEV);
 	}
 	if (status & STAT_BUSY) {
 
 		pr_err("Busy devcmd %d\n",  _CMD_N(cmd));
-		return -EBUSY;
+		return (EBUSY);
 	}
 
 	if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
@@ -214,7 +215,7 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 		status = ENIC_BUS_READ_4(devcmd, DEVCMD_STATUS);
 		if (status == 0xFFFFFFFF) {
 			/* PCI-e target device is gone */
-			return -ENODEV;
+			return (ENODEV);
 		}
 
 		if (!(status & STAT_BUSY)) {
@@ -225,7 +226,7 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 					pr_err("Devcmd %d failed " \
 						"with error code %d\n",
 						_CMD_N(cmd), err);
-				return err;
+				return (err);
 			}
 
 			if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
@@ -237,7 +238,82 @@ static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 	}
 
 	pr_err("Timedout devcmd %d\n", _CMD_N(cmd));
-	return -ETIMEDOUT;
+	return (ETIMEDOUT);
+}
+
+static int _vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+	int wait)
+{
+	struct devcmd2_controller *dc2c = vdev->devcmd2;
+	struct devcmd2_result *result;
+	u8 color;
+	unsigned int i;
+	u32 fetch_index, new_posted;
+	int delay, err;
+	u32 posted = dc2c->posted;
+
+	fetch_index = ENIC_BUS_READ_4(dc2c->wq_ctrl, TX_FETCH_INDEX);
+	if (fetch_index == 0xFFFFFFFF)
+		return (ENODEV);
+
+	new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+
+	if (new_posted == fetch_index) {
+		device_printf(dev_from_vnic_dev(vdev),
+		    "devcmd2 %d: wq is full. fetch index: %u, posted index: %u\n",
+		    _CMD_N(cmd), fetch_index, posted);
+		return (EBUSY);
+	}
+
+	dc2c->cmd_ring[posted].cmd = cmd;
+	dc2c->cmd_ring[posted].flags = 0;
+
+	if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+		dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
+	if (_CMD_DIR(cmd) & _CMD_DIR_WRITE)
+		for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+			dc2c->cmd_ring[posted].args[i] = vdev->args[i];
+
+	ENIC_BUS_WRITE_4(dc2c->wq_ctrl, TX_POSTED_INDEX, new_posted);
+	dc2c->posted = new_posted;
+
+	if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
+		return (0);
+
+	result = dc2c->result + dc2c->next_result;
+	color = dc2c->color;
+
+	dc2c->next_result++;
+	if (dc2c->next_result == dc2c->result_size) {
+		dc2c->next_result = 0;
+		dc2c->color = dc2c->color ? 0 : 1;
+	}
+
+	for (delay = 0; delay < wait; delay++) {
+		if (result->color == color) {
+			if (result->error) {
+				err = result->error;
+				if (err != ERR_ECMDUNKNOWN ||
+				     cmd != CMD_CAPABILITY)
+					device_printf(dev_from_vnic_dev(vdev),
+					     "Error %d devcmd %d\n", err,
+					     _CMD_N(cmd));
+				return (err);
+			}
+			if (_CMD_DIR(cmd) & _CMD_DIR_READ)
+				for (i = 0; i < VNIC_DEVCMD2_NARGS; i++)
+					vdev->args[i] = result->results[i];
+
+			return 0;
+		}
+		udelay(100);
+	}
+
+	device_printf(dev_from_vnic_dev(vdev),
+	    "devcmd %d timed out\n", _CMD_N(cmd));
+
+
+	return (ETIMEDOUT);
 }
 
 static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
@@ -253,7 +329,7 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 	 */
 	if (nargs > VNIC_DEVCMD_NARGS - 2) {
 		pr_err("number of args %d exceeds the maximum\n", nargs);
-		return -EINVAL;
+		return (EINVAL);
 	}
 	memset(vdev->args, 0, sizeof(vdev->args));
 
@@ -261,9 +337,9 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 	vdev->args[1] = cmd;
 	memcpy(&vdev->args[2], args, nargs * sizeof(args[0]));
 
-	err = _vnic_dev_cmd(vdev, proxy_cmd, wait);
+	err = vdev->devcmd_rtn(vdev, proxy_cmd, wait);
 	if (err)
-		return err;
+		return (err);
 
 	status = (u32)vdev->args[0];
 	if (status & STAT_ERROR) {
@@ -271,7 +347,7 @@ static int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
 		if (err != ERR_ECMDUNKNOWN ||
 		    cmd != CMD_CAPABILITY)
 			pr_err("Error %d proxy devcmd %d\n", err, _CMD_N(cmd));
-		return err;
+		return (err);
 	}
 
 	memcpy(args, &vdev->args[1], nargs * sizeof(args[0]));
@@ -286,16 +362,16 @@ static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
 
 	if (nargs > VNIC_DEVCMD_NARGS) {
 		pr_err("number of args %d exceeds the maximum\n", nargs);
-		return -EINVAL;
+		return (EINVAL);
 	}
 	memset(vdev->args, 0, sizeof(vdev->args));
 	memcpy(vdev->args, args, nargs * sizeof(args[0]));
 
-	err = _vnic_dev_cmd(vdev, cmd, wait);
+	err = vdev->devcmd_rtn(vdev, cmd, wait);
 
 	memcpy(args, vdev->args, nargs * sizeof(args[0]));
 
-	return err;
+	return (err);
 }
 
 int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
@@ -328,7 +404,7 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 		*a1 = args[1];
 	}
 
-	return err;
+	return (err);
 }
 
 int vnic_dev_cmd_args(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
@@ -400,7 +476,7 @@ int vnic_dev_capable_filter_mode(struct vnic_dev *vdev, u32 *mode,
 		args[1] = 0;
 		err = vnic_dev_cmd_args(vdev, CMD_CAPABILITY, args, 2, 1000);
 		if (err)
-			return err;
+			return (err);
 		max_level = args[1];
 		goto parse_max_level;
 	} else if (args[2] == FILTER_CAP_MODE_V1) {
@@ -479,7 +555,7 @@ int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, size_t size,
 		break;
 	}
 
-	return err;
+	return (err);
 }
 
 int vnic_dev_stats_clear(struct vnic_dev *vdev)
@@ -497,7 +573,7 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
 	int rc;
 
 	if (!vdev->stats)
*** 521 LINES SKIPPED ***