git: 86a88972dc31 - main - DPAA: Minor performance improvements

Justin Hibbits <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
The branch main has been updated by jhibbits:

URL: https://cgit.FreeBSD.org/src/commit/?id=86a88972dc311b5d4fd112564bfe19c6d7549b5f

commit 86a88972dc311b5d4fd112564bfe19c6d7549b5f
Author:     Justin Hibbits <[email protected]>
AuthorDate: 2026-07-26 04:01:51 +0000
Commit:     Justin Hibbits <[email protected]>
CommitDate: 2026-08-21 02:26:10 +0000

    DPAA: Minor performance improvements
    
    * Add interrupt coalescing for DQRR and MR, with thresholds and period
      as tunable sysctls under the `hw.qman` tree.
    * Do lazy/sloppy buffer management to avoid constantly checking
      thresholds via QMan portal round-trips.
    * Add cache stashing to prewarm caches, reducing latency.
    * Fix the definition of Context_A in the init_fq MC command/result
      structures, they're 64-bit fields, not 32-bit.
    * Reorder the dpaa_eth_frame_info as a bit of cleanup.
    * Take advantage of the fact that UMA small allocations are returned in
      the DMAP, and avoid pmap_kextract().
    
    These changes together improve throughput by ~1.5%
    (925Mbps->935-940Mbps) consistently, and reduce CPU usage by a bit,
    increasing idle CPU from 30%->35% minimum.
---
 sys/dev/dpaa/dpaa_eth.c     | 139 +++++++++++++++++++++++++++++++-------------
 sys/dev/dpaa/dpaa_eth.h     |   9 ++-
 sys/dev/dpaa/qman.c         |  28 ++++++++-
 sys/dev/dpaa/qman.h         |   3 +-
 sys/dev/dpaa/qman_portals.c |  88 ++++++++++++++++++++++++++++
 sys/dev/dpaa/qman_var.h     |   6 +-
 6 files changed, 227 insertions(+), 46 deletions(-)

diff --git a/sys/dev/dpaa/dpaa_eth.c b/sys/dev/dpaa/dpaa_eth.c
index 6424a6e0b0c3..2fb902c4d300 100644
--- a/sys/dev/dpaa/dpaa_eth.c
+++ b/sys/dev/dpaa/dpaa_eth.c
@@ -73,16 +73,42 @@
 #define DPAA_ETH_UNLOCK(sc)		mtx_unlock(&(sc)->sc_lock)
 #define DPAA_ETH_LOCK_ASSERT(sc)	mtx_assert(&(sc)->sc_lock, MA_OWNED)
 
+/*
+ * On 64-bit Book-E the direct map is always present, and the driver's
+ * UMA zones plus page-sized mbuf clusters live in it.  Bypass the
+ * page-table walk in pmap_kextract() for those; fall back for
+ * MJUM9BYTES/MJUM16BYTES clusters, which are kmem_alloc_contig()'d
+ * into KVA.
+ */
+static inline vm_paddr_t
+dpaa_eth_va_to_phys(vm_offset_t va)
+{
+	if (__predict_true(va >= DMAP_BASE_ADDRESS && va <= DMAP_MAX_ADDRESS))
+		return (DMAP_TO_PHYS(va));
+	return (pmap_kextract(va));
+}
+
 /**
  * @group dTSEC RM private defines.
  * @{
  */
 #define	DTSEC_BPOOLS_USED	(1)
 #define	DTSEC_MAX_TX_QUEUE_LEN	256
+/*
+ * Sample the hardware TX FQ counter every Nth packet.  The FQ counter is
+ * 24 bits and the soft cap above is 256, so overshoot by N is trivial.
+ */
+#define	DTSEC_MAX_TX_QUEUE_CHECK_INTERVAL	32
+/*
+ * Confirmation callback drain-detection.  Fast path (TX not backpressured)
+ * skips the MC call entirely; when flagged, we sample every Nth callback to
+ * detect the drain-to-zero transition.
+ */
+#define	DTSEC_TX_CONF_CHECK_INTERVAL		32
 
 struct dpaa_eth_frame_info {
-	struct mbuf			*fi_mbuf;
 	struct fman_internal_context	fi_ic;
+	struct mbuf			*fi_mbuf;
 	struct dpaa_sgte		fi_sgt[DPAA_NUM_OF_SG_TABLE_ENTRY];
 };
 
@@ -90,6 +116,13 @@ enum dpaa_eth_pool_params {
 	DTSEC_RM_POOL_RX_LOW_MARK	= 16,
 	DTSEC_RM_POOL_RX_HIGH_MARK	= 64,
 	DTSEC_RM_POOL_RX_MAX_SIZE	= 256,
+	/*
+	 * MAX_SIZE is a soft cap set well below the BMan hardware pool
+	 * limit, so sampling the depth every N put-backs per CPU is safe:
+	 * worst-case overshoot is N * ncpus buffers, still tiny vs. the
+	 * hardware pool.
+	 */
+	DTSEC_RM_POOL_RX_CHECK_INTERVAL	= 32,
 
 	DTSEC_RM_POOL_FI_LOW_MARK	= 16,
 	DTSEC_RM_POOL_FI_HIGH_MARK	= 64,
@@ -227,7 +260,7 @@ dtsec_add_buffers(struct dpaa_eth_softc *sc, int count)
 			b = uma_zalloc(sc->sc_rx_zone, M_NOWAIT);
 			if (b == NULL)
 				return (ENOMEM);
-			pa = pmap_kextract((vm_offset_t)b);
+			pa = DMAP_TO_PHYS((vm_offset_t)b);
 			bufs[i].buf_hi = (pa >> 32);
 			bufs[i].buf_lo = (pa & 0xffffffff);
 		}
@@ -273,6 +306,9 @@ dpaa_eth_pool_rx_free(struct dpaa_eth_softc *sc)
 
 	if (sc->sc_rx_zone != NULL)
 		uma_zdestroy(sc->sc_rx_zone);
+
+	free(sc->sc_rx_pool_check_cnt, M_DEVBUF);
+	sc->sc_rx_pool_check_cnt = NULL;
 }
 
 int
@@ -288,6 +324,10 @@ dpaa_eth_pool_rx_init(struct dpaa_eth_softc *sc)
 	sc->sc_rx_zone = uma_zcreate(sc->sc_rx_zname, MCLBYTES, NULL,
 	    NULL, NULL, NULL, MCLBYTES - 1, 0);
 
+	sc->sc_rx_pool_check_cnt = malloc_aligned(
+	    (mp_maxid + 1) * sizeof(struct dpaa_pcpu_cnt),
+	    CACHE_LINE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
+
 	sc->sc_rx_pool = bman_pool_create(&sc->sc_rx_bpid, MCLBYTES,
 	    DTSEC_RM_POOL_RX_MAX_SIZE, DTSEC_RM_POOL_RX_LOW_MARK,
 	    DTSEC_RM_POOL_RX_HIGH_MARK, 0, 0, dpaa_eth_pool_rx_depleted, sc);
@@ -316,11 +356,19 @@ dpaa_eth_fq_mext_free(struct mbuf *m)
 
 	buffer = m->m_ext.ext_arg1;
 	sc = m->m_ext.ext_arg2;
-	if (bman_count(sc->sc_rx_pool) <= DTSEC_RM_POOL_RX_MAX_SIZE)
-		bman_put_buffer(sc->sc_rx_pool,
-		    pmap_kextract((vm_offset_t)buffer), sc->sc_rx_bpid);
-	else
+	/*
+	 * Sloppy per-CPU sampling: no pin, no atomic.  A stray migration
+	 * between the curcpu read and the increment can only mis-attribute
+	 * one bump to the wrong CPU's counter; the sampling rate stays
+	 * within the acceptable slop window.
+	 */
+	if ((++sc->sc_rx_pool_check_cnt[curcpu].cnt &
+	    (DTSEC_RM_POOL_RX_CHECK_INTERVAL - 1)) == 0 &&
+	    bman_count(sc->sc_rx_pool) > DTSEC_RM_POOL_RX_MAX_SIZE)
 		dpaa_eth_pool_rx_put_buffer(sc, buffer, NULL);
+	else
+		bman_put_buffer(sc->sc_rx_pool,
+		    DMAP_TO_PHYS((vm_offset_t)buffer), sc->sc_rx_bpid);
 }
 
 static int
@@ -421,8 +469,6 @@ dpaa_eth_fq_tx_confirm_callback(device_t portal, struct qman_fq *fq,
 {
 	struct dpaa_eth_frame_info *fi;
 	struct dpaa_eth_softc *sc;
-	unsigned int qlen;
-	struct dpaa_sgte *sgt0;
 
 	sc = app;
 
@@ -434,25 +480,33 @@ dpaa_eth_fq_tx_confirm_callback(device_t portal, struct qman_fq *fq,
 	 * We are storing struct dpaa_eth_frame_info in first entry
 	 * of scatter-gather table.
 	 */
-	sgt0 = (struct dpaa_sgte *)PHYS_TO_DMAP(frame->addr + frame->offset);
-	fi = (struct dpaa_eth_frame_info *)PHYS_TO_DMAP(sgt0->addr);
+	fi = (struct dpaa_eth_frame_info *)PHYS_TO_DMAP(frame->addr);
 
 	/* Free transmitted frame */
 	m_freem(fi->fi_mbuf);
 	dpaa_eth_fi_free(sc, fi);
 
-	qlen = qman_fq_get_counter(sc->sc_tx_conf_fq, QMAN_COUNTER_FRAME);
-
-	if (qlen == 0) {
-		DPAA_ETH_LOCK(sc);
-
-		if (sc->sc_tx_fq_full) {
-			sc->sc_tx_fq_full = 0;
-			dpaa_eth_if_start_locked(sc);
-		}
-
-		DPAA_ETH_UNLOCK(sc);
+	/*
+	 * Fast path: TX isn't backpressured, so there's nothing to
+	 * restart.  Acquire load pairs with the release store on the
+	 * TX path so a concurrent set of the flag is observed here.
+	 */
+	if (atomic_load_acq_int(&sc->sc_tx_fq_full) == 0)
+		return (1);
+
+	/* Rate-limit the MC round-trip to detect drain-to-zero. */
+	if ((sc->sc_tx_conf_check_cnt++ &
+	    (DTSEC_TX_CONF_CHECK_INTERVAL - 1)) != 0)
+		return (1);
+	if (qman_fq_get_counter(sc->sc_tx_conf_fq, QMAN_COUNTER_FRAME) != 0)
+		return (1);
+
+	DPAA_ETH_LOCK(sc);
+	if (sc->sc_tx_fq_full) {
+		atomic_store_rel_int(&sc->sc_tx_fq_full, 0);
+		dpaa_eth_if_start_locked(sc);
 	}
+	DPAA_ETH_UNLOCK(sc);
 
 	return (1);
 }
@@ -484,8 +538,13 @@ dpaa_eth_fq_rx_init(struct dpaa_eth_softc *sc)
 	/* Default Frame Queue */
 	if (sc->sc_rx_channel == 0)
 		sc->sc_rx_channel = qman_alloc_channel();
+	/*
+	 * Stash 1 cacheline of frame annotation (parse result / IC) and
+	 * 1 of frame data head into the destination core's cache when
+	 * QMan dequeues an RX frame -- the RX callback reads both.
+	 */
 	fq = qman_fq_create(1, sc->sc_rx_channel, DTSEC_RM_FQR_RX_WQ,
-	    false, 0, false, false, true, false, 0, 0, 0);
+	    false, 0, false, false, true, false, 0, 0, 0, 1, 1);
 	if (fq == NULL) {
 		device_printf(sc->sc_dev,
 		    "could not create default RX queue\n");
@@ -529,7 +588,8 @@ dpaa_eth_fq_tx_init(struct dpaa_eth_softc *sc)
 
 	/* TX Frame Queue */
 	fq = qman_fq_create(1, sc->sc_port_tx_qman_chan,
-	    DTSEC_RM_FQR_TX_WQ, false, 0, false, false, true, false, 0, 0, 0);
+	    DTSEC_RM_FQR_TX_WQ, false, 0, false, false, true, false, 0, 0, 0,
+	    0, 0);
 	if (fq == NULL) {
 		device_printf(sc->sc_dev, "could not create default TX queue"
 		    "\n");
@@ -543,7 +603,7 @@ dpaa_eth_fq_tx_init(struct dpaa_eth_softc *sc)
 	/* TX Confirmation Frame Queue */
 	fq = qman_fq_create(1, sc->sc_rx_channel,
 	    DTSEC_RM_FQR_TX_CONF_WQ, false, 0, false, false, true, false, 0, 0,
-	    0);
+	    0, 0, 0);
 	if (fq == NULL) {
 		device_printf(sc->sc_dev, "could not create TX confirmation "
 		    "queue\n");
@@ -612,7 +672,7 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
 {
 	vm_size_t dsize, psize, ssize;
 	struct dpaa_eth_frame_info *fi;
-	unsigned int qlen, i;
+	unsigned int i;
 	struct mbuf *m0, *m;
 	vm_offset_t vaddr;
 	struct dpaa_fd fd;
@@ -626,12 +686,16 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
 	if ((if_getdrvflags(sc->sc_ifnet) & IFF_DRV_RUNNING) != IFF_DRV_RUNNING)
 		return;
 
-	while (!if_sendq_empty(sc->sc_ifnet)) {
-		/* Check length of the TX queue */
-		qlen = qman_fq_get_counter(sc->sc_tx_fq, QMAN_COUNTER_FRAME);
+	if (sc->sc_tx_fq_full)
+		return;
 
-		if (qlen >= DTSEC_MAX_TX_QUEUE_LEN) {
-			sc->sc_tx_fq_full = 1;
+	while (!if_sendq_empty(sc->sc_ifnet)) {
+		if ((sc->sc_tx_queue_check_cnt++ &
+		    (DTSEC_MAX_TX_QUEUE_CHECK_INTERVAL - 1)) == 0 &&
+		    qman_fq_get_counter(sc->sc_tx_fq, QMAN_COUNTER_FRAME) >=
+		    DTSEC_MAX_TX_QUEUE_LEN) {
+			atomic_store_rel_int(&sc->sc_tx_fq_full, 1);
+			sc->sc_tx_queue_check_cnt = 0;
 			return;
 		}
 
@@ -650,17 +714,11 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
 		psize = 0;
 		dsize = 0;
 		fi->fi_mbuf = m0;
+
 		while (m && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
 			if (m->m_len == 0)
 				continue;
 
-			/*
-			 * First entry in scatter-gather table is used to keep
-			 * pointer to frame info structure.
-			 */
-			fi->fi_sgt[i].addr = pmap_kextract((vm_offset_t)fi);
-			i++;
-
 			dsize = m->m_len;
 			vaddr = (vm_offset_t)m->m_data;
 			while (dsize > 0 && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
@@ -668,7 +726,7 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
 				if (m->m_len < ssize)
 					ssize = m->m_len;
 
-				fi->fi_sgt[i].addr = pmap_kextract(vaddr);
+				fi->fi_sgt[i].addr = dpaa_eth_va_to_phys(vaddr);
 				fi->fi_sgt[i].length = ssize;
 
 				fi->fi_sgt[i].extension = 0;
@@ -697,15 +755,14 @@ dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
 
 		fi->fi_sgt[i - 1].final = 1;
 
-		fd.addr = pmap_kextract((vm_offset_t)&fi->fi_ic);
+		fd.addr = DMAP_TO_PHYS((vm_offset_t)fi);
 		fd.length = psize;
 		fd.format = DPAA_FD_FORMAT_SHORT_MBSF;
 
 		fd.liodn = 0;
 		fd.bpid = 0;
 		fd.eliodn = 0;
-		fd.offset = offsetof(struct dpaa_eth_frame_info, fi_sgt) -
-		    offsetof(struct dpaa_eth_frame_info, fi_ic);
+		fd.offset = offsetof(struct dpaa_eth_frame_info, fi_sgt);
 		fd.cmd_stat = dpaa_eth_tx_add_csum(fi);
 
 		DPAA_ETH_UNLOCK(sc);
diff --git a/sys/dev/dpaa/dpaa_eth.h b/sys/dev/dpaa/dpaa_eth.h
index 7832b4dd7bff..cc46dc5485d6 100644
--- a/sys/dev/dpaa/dpaa_eth.h
+++ b/sys/dev/dpaa/dpaa_eth.h
@@ -27,6 +27,10 @@
 #ifndef DPAA_ETH_H_
 #define DPAA_ETH_H_
 
+struct dpaa_pcpu_cnt {
+	u_int	cnt;
+} __aligned(CACHE_LINE_SIZE);
+
 struct dpaa_eth_softc {
 	/* XXX MII bus requires that struct ifnet is first!!! */
 	if_t				sc_ifnet;
@@ -42,6 +46,7 @@ struct dpaa_eth_softc {
 	uint8_t				sc_rx_bpid;
 	uma_zone_t			sc_rx_zone;
 	char				sc_rx_zname[64];
+	struct dpaa_pcpu_cnt		*sc_rx_pool_check_cnt;	/* per-CPU */
 
 	/* RX Frame Queue */
 	struct qman_fq			*sc_rx_fq;
@@ -49,9 +54,11 @@ struct dpaa_eth_softc {
 
 	/* TX Frame Queue */
 	struct qman_fq			*sc_tx_fq;
-	bool				sc_tx_fq_full;
+	volatile u_int			sc_tx_fq_full;
+	u_int				sc_tx_queue_check_cnt;
 	struct qman_fq			*sc_tx_conf_fq;
 	uint32_t			sc_tx_conf_fqid;
+	u_int				sc_tx_conf_check_cnt;
 
 	/* Methods */
 	int				(*sc_port_rx_init)
diff --git a/sys/dev/dpaa/qman.c b/sys/dev/dpaa/qman.c
index 9143ebde5cb4..fe609711f413 100644
--- a/sys/dev/dpaa/qman.c
+++ b/sys/dev/dpaa/qman.c
@@ -149,6 +149,13 @@
 #define	QM_FQCTRL_HOLDACTIVE		0x0002
 #define	QM_FQCTRL_LIC			0x0001
 
+/*
+ * Context_A stashing config.
+ */
+#define	QM_STASHING_EXCL_ANNOTATION	0x04
+#define	QM_STASHING_EXCL_DATA		0x02
+#define	QM_STASHING_EXCL_CONTEXT	0x01
+
 #define	QMAN_CHANNEL_POOL1_REV1		0x21
 #define	QMAN_CHANNEL_POOL1_REV3		0x401
 
@@ -496,7 +503,8 @@ qman_fq_create(uint32_t fqids_num, int channel, uint8_t wq,
     bool force_fqid, uint32_t fqid_or_align, bool init_parked,
     bool hold_active, bool prefer_in_cache, bool congst_avoid_ena,
     void *congst_group, int8_t overhead_accounting_len,
-    uint32_t tail_drop_threshold)
+    uint32_t tail_drop_threshold,
+    uint8_t annotation_cl, uint8_t data_cl)
 {
 	union qman_mc_command cmd;
 	struct qman_softc *sc;
@@ -529,6 +537,24 @@ qman_fq_create(uint32_t fqids_num, int channel, uint8_t wq,
 	    (hold_active ? QM_FQCTRL_HOLDACTIVE : 0) |
 	    (congst_avoid_ena ? QM_FQCTRL_AVOIDBLOCK : 0);
 
+	/*
+	 * Configure hardware cache stashing: on dequeue, QMan will push
+	 * the requested number of cachelines of frame annotation and/or
+	 * frame data into the destination core's cache, hiding memory
+	 * latency for the RX callback.
+	 */
+	if (annotation_cl != 0 || data_cl != 0) {
+		uint64_t excl, cl;
+
+		excl = (annotation_cl != 0 ? QM_STASHING_EXCL_ANNOTATION : 0) |
+		    (data_cl != 0 ? QM_STASHING_EXCL_DATA : 0);
+		cl = ((annotation_cl & 3) << 4) | ((data_cl & 3) << 2);
+		/* excl in wire byte 0 (bits 63-56), cl in wire byte 1. */
+		cmd.init_fq.context_a = (excl << 56) | (cl << 48);
+		cmd.init_fq.fq_ctrl |= QM_FQCTRL_CTXASTASH;
+		cmd.init_fq.we_mask |= QCSP_INIT_FQ_WE_CONTEXT_A;
+	}
+
 	critical_enter();
 
 	/* Ensure we have got QMan port initialized */
diff --git a/sys/dev/dpaa/qman.h b/sys/dev/dpaa/qman.h
index 0e841dbc6ae6..d02e6d8bf94c 100644
--- a/sys/dev/dpaa/qman.h
+++ b/sys/dev/dpaa/qman.h
@@ -245,7 +245,8 @@ struct qman_fq *qman_fq_create(uint32_t fqids_num, int channel,
     uint8_t wq, bool force_fqid, uint32_t fqid_or_align, bool init_parked,
     bool hold_active, bool prefer_in_cache, bool congst_avoid_ena,
     void *congst_group, int8_t overhead_accounting_len,
-    uint32_t tail_drop_threshold);
+    uint32_t tail_drop_threshold,
+    uint8_t annotation_cl, uint8_t data_cl);
 
 /**
  * Free Frame Queue Range.
diff --git a/sys/dev/dpaa/qman_portals.c b/sys/dev/dpaa/qman_portals.c
index 3b64aca71cb7..e0a67410884e 100644
--- a/sys/dev/dpaa/qman_portals.c
+++ b/sys/dev/dpaa/qman_portals.c
@@ -16,6 +16,8 @@
 #include <sys/proc.h>
 #include <sys/pcpu.h>
 #include <sys/sched.h>
+#include <sys/smp.h>	/* For CPU_FOREACH() */
+#include <sys/sysctl.h>
 #include <ddb/ddb.h>
 
 #include <machine/bus.h>
@@ -115,6 +117,7 @@
 #define	QCSP_IER		0x0e04
 #define	QCSP_ISDR		0x0e08
 #define	QCSP_IIR		0xe0c
+#define	QCSP_ITPR		0x0e14
 
 #define	QM_EQCR_VERB_CMD_ENQUEUE	0x01
 #define	QM_EQCR_VERB_BIT_INT		0x04
@@ -130,6 +133,85 @@ DPAA_RING(qman_eqcr, QMAN_EQCR_COUNT, QCSP_EQCR_PI_CENA, QCSP_EQCR_CI_CENA,
 DPAA_RING(qman_dqrr, QMAN_DQRR_COUNT, QCSP_DQRR_PI_CENA, QCSP_DQRR_CI_CENA,
 		QCSP_DQRR_PI_CINH, QCSP_DQRR_CI_CINH);
 
+/*
+ *  dqrr_ithresh: fire DQRR interrupt once N (of 16) dequeues queued (1-15)
+ *  mr_ithresh:   fire MR interrupt after N messages (1-7)
+ *  iperiod:      timer bound on interrupt latency, in units of 256 QMan
+ *                clocks (~32 us on typical QorIQ platform clock at 100)
+ */
+static int qman_dqrr_ithresh = 12;
+static int qman_mr_ithresh = 4;
+static int qman_iperiod = 100;
+
+static void
+qman_portal_update_reg(bus_size_t reg, uint32_t val)
+{
+	struct qman_portal_softc *sc;
+	device_t portal;
+	int cpu;
+
+	CPU_FOREACH(cpu) {
+		portal = DPCPU_ID_GET(cpu, qman_affine_portal);
+		if (portal == NULL)
+			continue;
+		sc = device_get_softc(portal);
+		bus_write_4(sc->sc_base.sc_mres[1], reg, val);
+	}
+}
+
+static int
+qman_sysctl_reg(struct sysctl_oid *oidp, struct sysctl_req *req,
+    int *var, int min, int max, bus_size_t reg)
+{
+	int val, error;
+
+	val = *var;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	if (val < min || val > max)
+		return (EINVAL);
+	*var = val;
+	qman_portal_update_reg(reg, val);
+	return (0);
+}
+
+static int
+qman_sysctl_dqrr_ithresh(SYSCTL_HANDLER_ARGS)
+{
+	return (qman_sysctl_reg(oidp, req,
+	    &qman_dqrr_ithresh, 1, 15, QCSP_DQRR_ITR));
+}
+
+static int
+qman_sysctl_mr_ithresh(SYSCTL_HANDLER_ARGS)
+{
+	return (qman_sysctl_reg(oidp, req,
+	    &qman_mr_ithresh, 1, 7, QCSP_MR_ITR));
+}
+
+static int
+qman_sysctl_iperiod(SYSCTL_HANDLER_ARGS)
+{
+	return (qman_sysctl_reg(oidp, req,
+	    &qman_iperiod, 1, 65535, QCSP_ITPR));
+}
+
+SYSCTL_NODE(_hw, OID_AUTO, qman, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+    "QMan portal tunables");
+SYSCTL_PROC(_hw_qman, OID_AUTO, dqrr_ithresh,
+    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
+    NULL, 0, qman_sysctl_dqrr_ithresh, "I",
+    "DQRR interrupt threshold (dequeues queued before interrupt; 1-15)");
+SYSCTL_PROC(_hw_qman, OID_AUTO, mr_ithresh,
+    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
+    NULL, 0, qman_sysctl_mr_ithresh, "I",
+    "Message Ring interrupt threshold (messages queued before interrupt; 1-7)");
+SYSCTL_PROC(_hw_qman, OID_AUTO, iperiod,
+    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
+    NULL, 0, qman_sysctl_iperiod, "I",
+    "Interrupt time period (units of 256 QMan clocks)");
+
 /*
  * pmode: one of the CFG_EPM constants.
  * stash_prio: 0 or CFG_EP
@@ -208,6 +290,12 @@ qman_portal_attach(device_t dev, int cpu)
 	}
 	sc->sc_affine_channel = cell;
 	DPCPU_ID_SET(cpu, qman_affine_portal, dev);
+
+	/* Interrupt coalescing.  See tunables above. */
+	bus_write_4(sc->sc_base.sc_mres[1], QCSP_DQRR_ITR, qman_dqrr_ithresh);
+	bus_write_4(sc->sc_base.sc_mres[1], QCSP_MR_ITR, qman_mr_ithresh);
+	bus_write_4(sc->sc_base.sc_mres[1], QCSP_ITPR, qman_iperiod);
+
 	bus_write_4(sc->sc_base.sc_mres[1], QCSP_IER,
 	    QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI | QM_PIRQ_CSCI |
 	    QM_PIRQ_DQRI);
diff --git a/sys/dev/dpaa/qman_var.h b/sys/dev/dpaa/qman_var.h
index 8ed36bbb6a35..6d13340ca365 100644
--- a/sys/dev/dpaa/qman_var.h
+++ b/sys/dev/dpaa/qman_var.h
@@ -44,7 +44,7 @@ union qman_mc_command {
 		uint16_t ics_cred;
 		uint16_t td_thresh_oac;
 		uint32_t context_b;
-		uint32_t context_a;
+		uint64_t context_a;
 		uint8_t _rsvd1[32];
 	} init_fq;
 	struct {
@@ -92,7 +92,7 @@ union qman_mc_result {
 		uint16_t ics_cred;
 		uint16_t td_thresh;
 		uint32_t context_b;
-		uint32_t context_a;
+		uint64_t context_a;	/* 8 bytes on the wire */
 		uint16_t oac;
 		uint8_t _rsvd1[30];
 	} query_fq;
@@ -130,6 +130,8 @@ union qman_mc_result {
 		uint8_t _rsvd[61];
 	} alter_fqs;
 };
+_Static_assert(sizeof(union qman_mc_command) == 64, "MC command mis-sized");
+_Static_assert(sizeof(union qman_mc_result) == 64, "MC result mis-sized");
 
 struct qman_mc {
 	uint8_t polarity;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.