Newer iwm firmware requires the driver to use a feature known as dynamic queue allocation (DQA). What matters is that the command queue index was changed. Newer firmware images have stopped responding to commands sent with the old command queue index, and this is preventing us from dropping newer firmware versions into /etc/firmware without breaking things.
Some of our existing firmware images already support DQA (*-22 image files), and some do not (*-16 image files), so we need to support both modes of operation for now. (Linux has already removed non-DQA code paths from their iwlwifi driver). I have successfully tested this diff on 8265 with our current firmware image (22.361476.0) as well as a newer -22 firmware image (22.391740.0, which is *not* in fw_update yet). I have also tested 7265 successfully. Tests on 7260 and 8260 devices are still required. Nothing should change. At this point I am just looking for potential regressions when using this diff against our current firmware images. Reviews and OKs are also welcome. diff refs/heads/master refs/heads/dqa blob - a3998d706ae1895fcd6ba43f2f50f1bc69eabece blob + 1cabec763f7d55bfb4160279122586820b604780 --- sys/dev/pci/if_iwm.c +++ sys/dev/pci/if_iwm.c @@ -293,6 +293,7 @@ void iwm_nic_config(struct iwm_softc *); int iwm_nic_rx_init(struct iwm_softc *); int iwm_nic_tx_init(struct iwm_softc *); int iwm_nic_init(struct iwm_softc *); +int iwm_enable_ac_txq(struct iwm_softc *, int, int); int iwm_enable_txq(struct iwm_softc *, int, int, int); int iwm_post_alive(struct iwm_softc *); struct iwm_phy_db_entry *iwm_phy_db_get_section(struct iwm_softc *, uint16_t, @@ -360,6 +361,7 @@ int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_ty int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); int iwm_send_phy_cfg_cmd(struct iwm_softc *); int iwm_load_ucode_wait_alive(struct iwm_softc *, enum iwm_ucode_type); +int iwm_send_dqa_cmd(struct iwm_softc *); int iwm_run_init_mvm_ucode(struct iwm_softc *, int); int iwm_rx_addbuf(struct iwm_softc *, int, int); int iwm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); @@ -1186,8 +1188,23 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ 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. + * + * In non-DQA mode, we use rings 0 through 9 (0-3 are EDCA, 9 is cmd). + * + * Tx aggregation will require additional queues (one queue per TID + * for which aggregation is enabled) but we do not implement this yet. + * + * 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) return 0; @@ -1211,7 +1228,7 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ paddr += sizeof(struct iwm_device_cmd); /* FW commands may require more mapped space than packets. */ - if (qid == IWM_CMD_QUEUE) + if (qid == IWM_CMD_QUEUE || qid == IWM_DQA_CMD_QUEUE) mapsize = (sizeof(struct iwm_cmd_header) + IWM_MAX_CMD_PAYLOAD_SIZE); else @@ -1254,7 +1271,7 @@ iwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ ring->desc_dma.size, BUS_DMASYNC_PREWRITE); sc->qfullmsk &= ~(1 << ring->qid); /* 7000 family NICs are locked while commands are in progress. */ - if (ring->qid == IWM_CMD_QUEUE && ring->queued > 0) { + if (ring->qid == sc->cmdqid && ring->queued > 0) { if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) iwm_nic_unlock(sc); } @@ -1800,68 +1817,78 @@ iwm_nic_init(struct iwm_softc *sc) return 0; } +/* Map ieee80211_edca_ac categories to firmware Tx FIFO. */ const uint8_t iwm_ac_to_tx_fifo[] = { - IWM_TX_FIFO_VO, - IWM_TX_FIFO_VI, IWM_TX_FIFO_BE, IWM_TX_FIFO_BK, + IWM_TX_FIFO_VI, + IWM_TX_FIFO_VO, }; int -iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) +iwm_enable_ac_txq(struct iwm_softc *sc, int qid, int fifo) { iwm_nic_assert_locked(sc); IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - if (qid == IWM_CMD_QUEUE) { - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) - | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) + | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); - iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); - iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); - iwm_write_mem32(sc, - sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); + iwm_write_mem32(sc, + sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); - /* Set scheduler window size and frame limit. */ - iwm_write_mem32(sc, - sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + - sizeof(uint32_t), - ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((IWM_FRAME_LIMIT - << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + /* Set scheduler window size and frame limit. */ + iwm_write_mem32(sc, + sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + + sizeof(uint32_t), + ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((IWM_FRAME_LIMIT + << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | - IWM_SCD_QUEUE_STTS_REG_MSK); - } else { - struct iwm_scd_txq_cfg_cmd cmd; - int err; + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | + IWM_SCD_QUEUE_STTS_REG_MSK); - memset(&cmd, 0, sizeof(cmd)); - cmd.scd_queue = qid; - cmd.enable = 1; - cmd.sta_id = sta_id; - cmd.tx_fifo = fifo; - cmd.aggregate = 0; - cmd.window = IWM_FRAME_LIMIT; + if (qid == sc->cmdqid) + iwm_write_prph(sc, IWM_SCD_EN_CTRL, + iwm_read_prph(sc, IWM_SCD_EN_CTRL) | (1 << qid)); - err = iwm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, 0, - sizeof(cmd), &cmd); - if (err) - return err; - } + return 0; +} - iwm_write_prph(sc, IWM_SCD_EN_CTRL, - iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); +int +iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) +{ + struct iwm_scd_txq_cfg_cmd cmd; + int err; + iwm_nic_assert_locked(sc); + + IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); + + memset(&cmd, 0, sizeof(cmd)); + cmd.scd_queue = qid; + cmd.enable = 1; + cmd.sta_id = sta_id; + cmd.tx_fifo = fifo; + cmd.aggregate = 0; + cmd.window = IWM_FRAME_LIMIT; + + err = iwm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, 0, + sizeof(cmd), &cmd); + if (err) + return err; + return 0; } @@ -1895,7 +1922,7 @@ iwm_post_alive(struct iwm_softc *sc) iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); /* enable command channel */ - err = iwm_enable_txq(sc, 0 /* unused */, IWM_CMD_QUEUE, 7); + err = iwm_enable_ac_txq(sc, sc->cmdqid, IWM_TX_FIFO_CMD); if (err) goto out; @@ -3205,6 +3232,18 @@ iwm_send_phy_cfg_cmd(struct iwm_softc *sc) } int +iwm_send_dqa_cmd(struct iwm_softc *sc) +{ + struct iwm_dqa_enable_cmd dqa_cmd = { + .cmd_queue = htole32(IWM_DQA_CMD_QUEUE), + }; + uint32_t cmd_id; + + 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); +} + +int iwm_load_ucode_wait_alive(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { @@ -3215,6 +3254,11 @@ iwm_load_ucode_wait_alive(struct iwm_softc *sc, if (err) return err; + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) + sc->cmdqid = IWM_DQA_CMD_QUEUE; + else + sc->cmdqid = IWM_CMD_QUEUE; + sc->sc_uc_current = ucode_type; err = iwm_start_fw(sc, ucode_type); if (err) { @@ -3262,6 +3306,12 @@ iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justn if (err) return err; + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) { + err = iwm_send_dqa_cmd(sc); + if (err) + return err; + } + err = iwm_sf_config(sc, IWM_SF_INIT_OFF); if (err) return err; @@ -3825,7 +3875,7 @@ iwm_phy_ctxt_cmd(struct iwm_softc *sc, struct iwm_phy_ 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; struct iwm_device_cmd *cmd; @@ -4068,10 +4118,10 @@ iwm_free_resp(struct iwm_softc *sc, struct iwm_host_cm void iwm_cmd_done(struct iwm_softc *sc, int qid, int idx, int code) { - 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 (qid != IWM_CMD_QUEUE) { + if (qid != sc->cmdqid) { return; /* Not a command ack. */ } @@ -4222,7 +4272,21 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie tid = 0; - ring = &sc->txq[ac]; + /* + * Map EDCA categories to Tx data queues. + * + * We use static data queue assignments even in DQA mode. We do not + * need to share Tx queues between stations because we only implement + * client mode; the firmware's station table contains only one entry + * which represents our access point. + * + * Tx aggregation will require additional queues (one queue per TID + * for which aggregation is enabled) but we do not implement this yet. + */ + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) + ring = &sc->txq[IWM_DQA_MIN_MGMT_QUEUE + ac]; + else + ring = &sc->txq[ac]; desc = &ring->desc[ring->cur]; memset(desc, 0, sizeof(*desc)); data = &ring->data[ring->cur]; @@ -4409,10 +4473,10 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie } int -iwm_flush_tx_path(struct iwm_softc *sc, int tfd_msk) +iwm_flush_tx_path(struct iwm_softc *sc, int tfd_queue_msk) { struct iwm_tx_path_flush_cmd flush_cmd = { - .queues_ctl = htole32(tfd_msk), + .queues_ctl = htole32(tfd_queue_msk), .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), }; int err; @@ -4622,8 +4686,11 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node if (!update) { int ac; for (ac = 0; ac < EDCA_NUM_AC; ac++) { - add_sta_cmd.tfd_queue_msk |= - htole32(1 << iwm_ac_to_tx_fifo[ac]); + int qid = ac; + if (isset(sc->sc_enabled_capa, + 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); } @@ -4679,7 +4746,7 @@ iwm_add_aux_sta(struct iwm_softc *sc) int err; uint32_t status; - err = iwm_enable_txq(sc, 0, IWM_AUX_QUEUE, IWM_TX_FIFO_MCAST); + err = iwm_enable_ac_txq(sc, IWM_AUX_QUEUE, IWM_TX_FIFO_MCAST); if (err) return err; @@ -5706,7 +5773,7 @@ iwm_deauth(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct iwm_node *in = (void *)ic->ic_bss; - int ac, tfd_msk, err; + int ac, tfd_queue_msk, err; splassert(IPL_NET); @@ -5722,10 +5789,15 @@ iwm_deauth(struct iwm_softc *sc) sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE; } - tfd_msk = 0; - for (ac = 0; ac < EDCA_NUM_AC; ac++) - tfd_msk |= htole32(1 << iwm_ac_to_tx_fifo[ac]); - err = iwm_flush_tx_path(sc, tfd_msk); + tfd_queue_msk = 0; + for (ac = 0; ac < EDCA_NUM_AC; ac++) { + int qid = ac; + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) + qid += IWM_DQA_MIN_MGMT_QUEUE; + tfd_queue_msk |= htole32(1 << qid); + } + + err = iwm_flush_tx_path(sc, tfd_queue_msk); if (err) { printf("%s: could not flush Tx path (error %d)\n", DEVNAME(sc), err); @@ -6441,7 +6513,12 @@ iwm_init_hw(struct iwm_softc *sc) } for (ac = 0; ac < EDCA_NUM_AC; ac++) { - err = iwm_enable_txq(sc, IWM_STATION_ID, ac, + int qid; + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_DQA_SUPPORT)) + qid = ac + IWM_DQA_MIN_MGMT_QUEUE; + else + qid = ac; + err = iwm_enable_txq(sc, IWM_STATION_ID, qid, iwm_ac_to_tx_fifo[ac]); if (err) { printf("%s: could not enable Tx queue %d (error %d)\n", @@ -7255,6 +7332,9 @@ iwm_notif_intr(struct iwm_softc *sc) break; } + + case IWM_WIDE_ID(IWM_DATA_PATH_GROUP, IWM_DQA_ENABLE_CMD): + break; default: handled = 0; blob - a223225b82955b698f5eb053b5feb3f2f0ab4e7d blob + 4d0f73d6130fb2e96eb14c36a02fbb09fde32ca9 --- sys/dev/pci/if_iwmreg.h +++ sys/dev/pci/if_iwmreg.h @@ -1655,7 +1655,42 @@ struct iwm_agn_scd_bc_tbl { /* Maximum number of Tx queues. */ #define IWM_MAX_QUEUES 31 -/* Tx queue numbers */ +/** + * DQA - Dynamic Queue Allocation -introduction + * + * Dynamic Queue Allocation (AKA "DQA") is a feature implemented in iwlwifi + * to allow dynamic allocation of queues on-demand, rather than allocate them + * statically ahead of time. Ideally, we would like to allocate one queue + * per RA/TID, thus allowing an AP - for example - to send BE traffic to STA2 + * even if it also needs to send traffic to a sleeping STA1, without being + * blocked by the sleeping station. + * + * Although the queues in DQA mode are dynamically allocated, there are still + * some queues that are statically allocated: + * TXQ #0 - command queue + * TXQ #1 - aux frames + * TXQ #2 - P2P device frames + * TXQ #3 - P2P GO/SoftAP GCAST/BCAST frames + * TXQ #4 - BSS DATA frames queue + * TXQ #5-8 - non-QoS data, QoS no-data, and MGMT frames queue pool + * TXQ #9 - P2P GO/SoftAP probe responses + * TXQ #10-31 - QoS DATA frames queue pool (for Tx aggregation) + */ + +/* static DQA Tx queue numbers */ +#define IWM_DQA_CMD_QUEUE 0 +#define IWM_DQA_AUX_QUEUE 1 +#define IWM_DQA_P2P_DEVICE_QUEUE 2 +#define IWM_DQA_INJECT_MONITOR_QUEUE 2 +#define IWM_DQA_GCAST_QUEUE 3 +#define IWM_DQA_BSS_CLIENT_QUEUE 4 +#define IWM_DQA_MIN_MGMT_QUEUE 5 +#define IWM_DQA_MAX_MGMT_QUEUE 8 +#define IWM_DQA_AP_PROBE_RESP_QUEUE 9 +#define IWM_DQA_MIN_DATA_QUEUE 10 +#define IWM_DQA_MAX_DATA_QUEUE 31 + +/* legacy non-DQA queues; the legacy command queue uses a different number! */ #define IWM_OFFCHANNEL_QUEUE 8 #define IWM_CMD_QUEUE 9 #define IWM_AUX_QUEUE 15 @@ -1818,6 +1853,17 @@ struct iwm_agn_scd_bc_tbl { #define IWM_REPLY_MAX 0xff +/* DATA_PATH group subcommand IDs */ +#define IWM_DQA_ENABLE_CMD 0x00 + +/* + * struct iwm_dqa_enable_cmd + * @cmd_queue: the TXQ number of the command queue + */ +struct iwm_dqa_enable_cmd { + uint32_t cmd_queue; +} __packed; /* DQA_CONTROL_CMD_API_S_VER_1 */ + /** * struct iwm_cmd_response - generic response struct for most commands * @status: status of the command asked, changes for each one @@ -5824,6 +5870,7 @@ iwm_cmd_id(uint8_t opcode, uint8_t groupid, uint8_t ve /* due to the conversion, this group is special */ #define IWM_ALWAYS_LONG_GROUP 1 #define IWM_SYSTEM_GROUP 4 +#define IWM_DATA_PATH_GROUP 5 struct iwm_cmd_header { uint8_t code; blob - e6593fbc840833a36d5a792042faaa85e3ac9719 blob + 7334214784fed3e047a66bebc6f6a3baa5685db1 --- sys/dev/pci/if_iwmvar.h +++ sys/dev/pci/if_iwmvar.h @@ -388,6 +388,7 @@ struct iwm_softc { struct iwm_tx_ring txq[IWM_MAX_QUEUES]; struct iwm_rx_ring rxq; int qfullmsk; + int cmdqid; int sc_sf_state;