git: 8d53176ef83d - main - iwm: add DQA (dynamic queue assignment)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 15 Oct 2026 02:19:04 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=8d53176ef83dad3a0962cdffdae98871c0379c35
commit 8d53176ef83dad3a0962cdffdae98871c0379c35
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2026-10-15 01:48:49 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-10-15 01:48:50 +0000
iwm: add DQA (dynamic queue assignment)
This is the minimum set of DQA bits from iwm at the given openbsd
commit place.
* add a method to map ac to queue / ring
* use the DQA command queue if needed
* glue to wire it up
Locally tested:
* Intel 7260, STA mode
* Intel 9260, STA mode
* Intel 8260, STA mode
* Intel 3160, STA mode
Obtained from: OpenBSD a6e25c727a2214219715b51ba6af2221d7e0d674
Differential Revision: https://reviews.freebsd.org/D60080
---
sys/dev/iwm/if_iwm.c | 87 +++++++++++++++++++++++++++++++++++++++--------
sys/dev/iwm/if_iwm_sta.c | 28 +++++++++++----
sys/dev/iwm/if_iwm_util.c | 2 +-
sys/dev/iwm/if_iwmvar.h | 1 +
4 files changed, 97 insertions(+), 21 deletions(-)
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c
index ddf781d247b6..610156681272 100644
--- a/sys/dev/iwm/if_iwm.c
+++ b/sys/dev/iwm/if_iwm.c
@@ -1147,10 +1147,25 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
ring->desc = ring->desc_dma.vaddr;
/*
- * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need
- * to allocate commands space for other rings.
+ * There is no need to allocate DMA buffers for unused rings.
+ * 7k/8k/9k hardware supports up to 31 Tx rings which is more
+ * than we currently need.
+ *
+ * In DQA mode we use 1 command queue + 4 DQA mgmt/data queues.
+ * The command is queue 0 (sc->txq[0]), and 4 mgmt/data frame queues
+ * are sc->tqx[IWM_DQA_MIN_MGMT_QUEUE + ac], i.e. sc->txq[5:8],
+ * in order to provide one queue per EDCA category.
+ * Tx aggregation requires additional queues, one queue per TID for
+ * which aggregation is enabled. We map TID 0-7 to sc->txq[10:17].
+ *
+ * In non-DQA mode, we use rings 0 through 9 (0-3 are EDCA, 9 is cmd),
+ * and Tx aggregation is not supported.
+ *
+ * Unfortunately, we cannot tell if DQA will be used until the
+ * firmware gets loaded later, so just allocate sufficient rings
+ * in order to satisfy both cases.
*/
- if (qid > IWM_CMD_QUEUE)
+ if (qid > IWM_LAST_AGG_TX_QUEUE)
return 0;
size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd);
@@ -1162,8 +1177,13 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid)
}
ring->cmd = ring->cmd_dma.vaddr;
- /* FW commands may require more mapped space than packets. */
- if (qid == IWM_CMD_QUEUE) {
+ /*
+ * FW commands may require more mapped space than packets. The
+ * DQA command queue is ring 0 and we can't tell at allocation
+ * time which command queue id will be used, so give both the
+ * command-sized DMA tag.
+ */
+ if (qid == IWM_CMD_QUEUE || qid == IWM_DQA_CMD_QUEUE) {
maxsize = IWM_RBUF_SIZE;
nsegments = 1;
} else {
@@ -1227,7 +1247,7 @@ iwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
ring->queued = 0;
ring->cur = 0;
- if (ring->qid == IWM_CMD_QUEUE && sc->cmd_hold_nic_awake)
+ if (ring->qid == sc->cmdqid && sc->cmd_hold_nic_awake)
iwm_pcie_clear_cmd_in_flight(sc);
}
@@ -1692,9 +1712,11 @@ iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0);
- if (qid == IWM_CMD_QUEUE) {
+ if (qid == sc->cmdqid) {
+#if 0
/* Disable the scheduler. */
iwm_write_prph(sc, IWM_SCD_EN_CTRL, 0);
+#endif
/* Stop the TX queue prior to configuration. */
iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid),
@@ -1737,7 +1759,8 @@ iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
IWM_SCD_QUEUE_STTS_REG_MSK);
/* Enable the scheduler for this queue. */
- iwm_write_prph(sc, IWM_SCD_EN_CTRL, qmsk);
+ iwm_write_prph(sc, IWM_SCD_EN_CTRL,
+ iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qmsk);
} else {
struct iwm_scd_txq_cfg_cmd cmd;
int error;
@@ -1813,7 +1836,8 @@ iwm_trans_pcie_fw_alive(struct iwm_softc *sc, uint32_t scd_base_addr)
iwm_nic_unlock(sc);
/* enable command channel */
- error = iwm_enable_txq(sc, 0 /* unused */, IWM_CMD_QUEUE, 7);
+ error = iwm_enable_txq(sc, 0 /* unused */, sc->cmdqid,
+ IWM_TX_FIFO_CMD);
if (error)
return error;
@@ -2929,6 +2953,11 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc,
sc->cur_ucode = ucode_type;
sc->ucode_loaded = FALSE;
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ sc->cmdqid = IWM_DQA_CMD_QUEUE;
+ else
+ sc->cmdqid = IWM_CMD_QUEUE;
+
memset(&alive_data, 0, sizeof(alive_data));
iwm_init_notification_wait(sc->sc_notif_wait, &alive_wait,
alive_cmd, nitems(alive_cmd),
@@ -3672,10 +3701,10 @@ iwm_rx_tx_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
static void
iwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
{
- struct iwm_tx_ring *ring = &sc->txq[IWM_CMD_QUEUE];
+ struct iwm_tx_ring *ring = &sc->txq[sc->cmdqid];
struct iwm_tx_data *data;
- if (pkt->hdr.qid != IWM_CMD_QUEUE) {
+ if (pkt->hdr.qid != sc->cmdqid) {
return; /* Not a command ack. */
}
@@ -3851,13 +3880,22 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac)
int nsegs;
uint8_t tid, type;
int i, totlen, error, pad;
+ int qid;
wh = mtod(m, struct ieee80211_frame *);
hdrlen = ieee80211_anyhdrsize(wh);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
tid = 0;
- ring = &sc->txq[ac];
+
+ /* Map EDCA categories to Tx data queues. */
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ qid = IWM_DQA_MIN_MGMT_QUEUE + ac;
+ else
+ qid = ac;
+
+ ring = &sc->txq[qid];
desc = &ring->desc[ring->cur];
+ memset(desc, 0, sizeof(*desc));
data = &ring->data[ring->cur];
/* Fill out iwm_tx_cmd to send to the firmware */
@@ -4767,6 +4805,17 @@ iwm_send_soc_conf(struct iwm_softc *sc)
return err;
}
+static int
+iwm_send_dqa_cmd(struct iwm_softc *sc)
+{
+ struct iwm_dqa_enable_cmd dqa_cmd;
+ uint32_t cmd_id;
+
+ dqa_cmd.cmd_queue = htole32(IWM_DQA_CMD_QUEUE);
+ cmd_id = iwm_cmd_id(IWM_DQA_ENABLE_CMD, IWM_DATA_PATH_GROUP, 0);
+ return iwm_send_cmd_pdu(sc, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd);
+}
+
static int
iwm_send_temp_report_ths_cmd(struct iwm_softc *sc)
{
@@ -4907,7 +4956,7 @@ static int
iwm_init_hw(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
- int error, i, ac;
+ int error, i, ac, qid;
sc->sf_state = IWM_SF_UNINIT;
@@ -4973,6 +5022,11 @@ iwm_init_hw(struct iwm_softc *sc)
goto error;
}
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) {
+ if ((error = iwm_send_dqa_cmd(sc)) != 0)
+ goto error;
+ }
+
/* Add auxiliary station for scanning */
if ((error = iwm_add_aux_sta(sc)) != 0) {
device_printf(sc->sc_dev, "add_aux_sta failed\n");
@@ -5011,7 +5065,12 @@ iwm_init_hw(struct iwm_softc *sc)
/* Enable Tx queues. */
for (ac = 0; ac < WME_NUM_AC; ac++) {
- error = iwm_enable_txq(sc, IWM_STATION_ID, ac,
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ qid = ac + IWM_DQA_MIN_MGMT_QUEUE;
+ else
+ qid = ac;
+
+ error = iwm_enable_txq(sc, IWM_STATION_ID, qid,
iwm_ac_to_tx_fifo[ac]);
if (error)
goto error;
diff --git a/sys/dev/iwm/if_iwm_sta.c b/sys/dev/iwm/if_iwm_sta.c
index 4c0fbd24658c..1c2725d33f90 100644
--- a/sys/dev/iwm/if_iwm_sta.c
+++ b/sys/dev/iwm/if_iwm_sta.c
@@ -162,8 +162,10 @@ iwm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in,
if (!update) {
int ac;
for (ac = 0; ac < WME_NUM_AC; ac++) {
- add_sta_cmd.tfd_queue_msk |=
- htole32(1 << iwm_ac_to_tx_fifo[ac]);
+ int qid = ac;
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ qid += IWM_DQA_MIN_MGMT_QUEUE;
+ add_sta_cmd.tfd_queue_msk |= htole32(1 << qid);
}
IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
}
@@ -277,7 +279,10 @@ iwm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap,
if (ret)
return ret;
for (ac = 0; ac < WME_NUM_AC; ac++) {
- tfd_queue_msk |= htole32(1 << iwm_ac_to_tx_fifo[ac]);
+ int qid = ac;
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ qid += IWM_DQA_MIN_MGMT_QUEUE;
+ tfd_queue_msk |= htole32(1 << qid);
}
ret = iwm_flush_tx_path(sc, tfd_queue_msk, IWM_CMD_SYNC);
if (ret)
@@ -352,13 +357,24 @@ int
iwm_add_aux_sta(struct iwm_softc *sc)
{
int ret;
+ int qid;
+
+ /*
+ * Note: OpenBSD calls different TXQ setups here;
+ * + DQA calls iwm_enable_txq(), which only sends the cmd_pdu
+ * + non-DQA calls iwm_enable_ac_txq(), which does similar stuff to
+ * command setup but not the command scheduler
+ */
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_DQA_SUPPORT))
+ qid = IWM_DQA_AUX_QUEUE;
+ else
+ qid = IWM_AUX_QUEUE;
sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID;
- sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_AUX_QUEUE);
+ sc->sc_aux_sta.tfd_queue_msk = (1 << qid);
/* Map Aux queue to fifo - needs to happen before adding Aux station */
- ret = iwm_enable_txq(sc, IWM_AUX_STA_ID, IWM_AUX_QUEUE,
- IWM_TX_FIFO_MCAST);
+ ret = iwm_enable_txq(sc, IWM_AUX_STA_ID, qid, IWM_TX_FIFO_MCAST);
if (ret)
return ret;
diff --git a/sys/dev/iwm/if_iwm_util.c b/sys/dev/iwm/if_iwm_util.c
index 305a5289f4a5..7744433312d4 100644
--- a/sys/dev/iwm/if_iwm_util.c
+++ b/sys/dev/iwm/if_iwm_util.c
@@ -167,7 +167,7 @@
int
iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd)
{
- struct iwm_tx_ring *ring = &sc->txq[IWM_CMD_QUEUE];
+ struct iwm_tx_ring *ring = &sc->txq[sc->cmdqid];
struct iwm_tfd *desc;
struct iwm_tx_data *txdata = NULL;
struct iwm_device_cmd *cmd;
diff --git a/sys/dev/iwm/if_iwmvar.h b/sys/dev/iwm/if_iwmvar.h
index 4231b394cfe4..8f2760f114f3 100644
--- a/sys/dev/iwm/if_iwmvar.h
+++ b/sys/dev/iwm/if_iwmvar.h
@@ -454,6 +454,7 @@ struct iwm_softc {
struct iwm_tx_ring txq[IWM_MAX_QUEUES];
struct iwm_rx_ring rxq;
int qfullmsk;
+ int cmdqid;
/* ICT table. */
struct iwm_dma_info ict_dma;