once again: iwm(4) multi-frame rx + monitor mode

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

once again: iwm(4) multi-frame rx + monitor mode

Stefan Sperling-5
I would like to try this again: In iwm(4), process more than one frame
per Rx interrupt, and enable monitor mode.

Monitor mode triggers "unhandled fimware response" errors without multi-Rx
support. We have seen these infamous errors in other contexts as well.
It is possible that the firmware decides to use multi-Rx in such cases
which would of course confuse our driver.

The fact is that Linux has not used single-Rx mode for years. One of their
developers told me that Intel has stopped testing it entirely. The current
code isn't future-proof and is likely to break with newer versions of
firmware and/or hardware.

These changes were attempted and reverted twice already.
Last time because they were causing throughput issues (and I now see why).
I have rebased my old diff and tweaked it slightly to avoid an unneeded
mbuf copy and reworked all the offset/length calculations once again.

Works fine here against several APs with no observable drop in throughput.

Please test this on as many iwm devices as possible. Thanks!


diff refs/heads/master refs/heads/iwm-multirx
blob - 42cb00c62cc455d871da09560dcf82876db2f4c0
blob + c9a4dc36c3a7cf09c9836141b6a966370ac94824
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -367,6 +367,7 @@ int iwm_get_signal_strength(struct iwm_softc *, struct
 void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *,
     struct iwm_rx_data *);
 int iwm_get_noise(const struct iwm_statistics_rx_non_phy *);
+int iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t);
 int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
     struct ieee80211_node *);
 void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
@@ -431,7 +432,7 @@ uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int)
 int iwm_rval2ridx(int);
 void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
 void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *,
-    struct iwm_mac_ctx_cmd *, uint32_t, int);
+    struct iwm_mac_ctx_cmd *, uint32_t);
 void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
     struct iwm_mac_data_sta *, int);
 int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
@@ -476,6 +477,9 @@ const char *iwm_desc_lookup(uint32_t);
 void iwm_nic_error(struct iwm_softc *);
 void iwm_nic_umac_error(struct iwm_softc *);
 #endif
+void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t);
+int iwm_rx_pkt_valid(struct iwm_rx_packet *);
+void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *);
 void iwm_notif_intr(struct iwm_softc *);
 int iwm_intr(void *);
 int iwm_match(struct device *, void *, void *);
@@ -1729,7 +1733,6 @@ iwm_nic_rx_init(struct iwm_softc *sc)
     IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
     IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |  /* HW bug */
     IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
-    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
     (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
     IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
     IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
@@ -3514,57 +3517,23 @@ iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, s
  return 0;
 }
 
-void
-iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
-    struct iwm_rx_data *data)
+int
+iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status)
 {
  struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = IC2IFP(ic);
  struct ieee80211_frame *wh;
  struct ieee80211_node *ni;
  struct ieee80211_rxinfo rxi;
  struct ieee80211_channel *bss_chan;
- struct mbuf *m;
  struct iwm_rx_phy_info *phy_info;
- struct iwm_rx_mpdu_res_start *rx_res;
  int device_timestamp;
- uint32_t len;
- uint32_t rx_pkt_status;
  int rssi, chanidx;
  uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
 
- bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
-    BUS_DMASYNC_POSTREAD);
-
  phy_info = &sc->sc_last_phy_info;
- rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
- wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
- len = le16toh(rx_res->byte_count);
- if (len < IEEE80211_MIN_LEN) {
- ic->ic_stats.is_rx_tooshort++;
- IC2IFP(ic)->if_ierrors++;
- return;
- }
- if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) {
- IC2IFP(ic)->if_ierrors++;
- return;
- }
- rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
-    sizeof(*rx_res) + len));
-
  if (__predict_false(phy_info->cfg_phy_cnt > 20))
- return;
+ return EINVAL;
 
- if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
-    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
- return; /* drop */
-
- m = data->m;
- if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
- return;
- m->m_data = pkt->data + sizeof(*rx_res);
- m->m_pkthdr.len = m->m_len = len;
-
  device_timestamp = le32toh(phy_info->system_timestamp);
 
  if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
@@ -3579,6 +3548,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
  if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))
  chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
 
+ wh = mtod(m, struct ieee80211_frame *);
  ni = ieee80211_find_rxnode(ic, wh);
  if (ni == ic->ic_bss) {
  /*
@@ -3603,7 +3573,6 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
  if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) !=
     IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
  ic->ic_stats.is_ccmp_dec_errs++;
- ifp->if_ierrors++;
  m_freem(m);
  goto done;
  }
@@ -3614,12 +3583,10 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
     (IWM_RX_MPDU_RES_STATUS_DEC_DONE |
     IWM_RX_MPDU_RES_STATUS_MIC_OK)) {
  ic->ic_stats.is_ccmp_dec_errs++;
- ifp->if_ierrors++;
  m_freem(m);
  goto done;
  }
  if (iwm_ccmp_decap(sc, m, ni) != 0) {
- ifp->if_ierrors++;
  m_freem(m);
  goto done;
  }
@@ -3691,6 +3658,8 @@ done:
  if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
  ni->ni_chan = bss_chan;
  ieee80211_release_node(ic, ni);
+
+ return 0;
 }
 
 void
@@ -4666,6 +4635,7 @@ void
 iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in,
     struct iwm_mac_power_cmd *cmd)
 {
+ struct ieee80211com *ic = &sc->sc_ic;
  struct ieee80211_node *ni = &in->in_ni;
  int dtim_period, dtim_msec, keep_alive;
 
@@ -4687,7 +4657,8 @@ iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_n
  keep_alive = roundup(keep_alive, 1000) / 1000;
  cmd->keep_alive_seconds = htole16(keep_alive);
 
- cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+ if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
 }
 
 int
@@ -4714,13 +4685,15 @@ iwm_power_mac_update_mode(struct iwm_softc *sc, struct
 int
 iwm_power_update_device(struct iwm_softc *sc)
 {
- struct iwm_device_power_cmd cmd = {
- .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
- };
+ struct iwm_device_power_cmd cmd = { };
+ struct ieee80211com *ic = &sc->sc_ic;
 
  if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
  return 0;
 
+ if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
  return iwm_send_cmd_pdu(sc,
     IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
 }
@@ -4782,7 +4755,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node
  add_sta_cmd.tfd_queue_msk |=
     htole32(1 << iwm_ac_to_tx_fifo[ac]);
  }
- IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
+    etherbroadcastaddr);
+ else
+ IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
+    in->in_ni.ni_bssid);
  }
  add_sta_cmd.add_modify = update ? 1 : 0;
  add_sta_cmd.station_flags_msk
@@ -5452,7 +5430,7 @@ iwm_ack_rates(struct iwm_softc *sc, struct iwm_node *i
 
 void
 iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
-    struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc)
+    struct iwm_mac_ctx_cmd *cmd, uint32_t action)
 {
 #define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
  struct ieee80211com *ic = &sc->sc_ic;
@@ -5464,12 +5442,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct i
     in->in_color));
  cmd->action = htole32(action);
 
- cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER);
+ else if (ic->ic_opmode == IEEE80211_M_STA)
+ cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+ else
+ panic("unsupported operating mode %d\n", ic->ic_opmode);
  cmd->tsf_id = htole32(IWM_TSF_ID_A);
 
  IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr);
- IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
+ return;
+ }
 
+ IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
  iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
  cmd->cck_rates = htole32(cck_ack_rates);
  cmd->ofdm_rates = htole32(ofdm_ack_rates);
@@ -5560,6 +5547,7 @@ int
 iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action,
     int assoc)
 {
+ struct ieee80211com *ic = &sc->sc_ic;
  struct ieee80211_node *ni = &in->in_ni;
  struct iwm_mac_ctx_cmd cmd;
  int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE);
@@ -5571,11 +5559,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node
 
  memset(&cmd, 0, sizeof(cmd));
 
- iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc);
+ iwm_mac_ctxt_cmd_common(sc, in, &cmd, action);
 
- /* Allow beacons to pass through as long as we are not associated or we
- * do not have dtim period information */
- if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC |
+    IWM_MAC_FILTER_IN_CONTROL_AND_MGMT |
+    IWM_MAC_FILTER_IN_BEACON |
+    IWM_MAC_FILTER_IN_PROBE_REQUEST |
+    IWM_MAC_FILTER_IN_CRC32);
+ } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+ /*
+ * Allow beacons to pass through as long as we are not
+ * associated or we do not have dtim period information.
+ */
  cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
  else
  iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
@@ -5792,7 +5788,10 @@ iwm_auth(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
- sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
+ else
+ sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
  err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
     IWM_FW_CTXT_ACTION_MODIFY, 0);
  if (err) {
@@ -5826,6 +5825,9 @@ iwm_auth(struct iwm_softc *sc)
  }
  sc->sc_flags |= IWM_FLAG_STA_ACTIVE;
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ return 0;
+
  /*
  * Prevent the FW from wandering off channel during association
  * by "protecting" the session with a time event.
@@ -5956,8 +5958,16 @@ iwm_run(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ /* Add a MAC context and a sniffing STA. */
+ err = iwm_auth(sc);
+ if (err)
+ return err;
+ }
+
  /* Configure Rx chains for MIMO. */
- if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
+ if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
+    (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
     !sc->sc_nvm.sku_cap_mimo_disable) {
  err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
     2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0);
@@ -6025,6 +6035,11 @@ iwm_run(struct iwm_softc *sc)
  ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
  ieee80211_mira_node_init(&in->in_mn);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ iwm_led_blink_start(sc);
+ return 0;
+ }
+
  /* Start at lowest available bit-rate, AMRR will raise. */
  in->in_ni.ni_txrate = 0;
  in->in_ni.ni_txmcs = 0;
@@ -6044,6 +6059,9 @@ iwm_run_stop(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ iwm_led_blink_stop(sc);
+
  err = iwm_sf_config(sc, IWM_SF_INIT_OFF);
  if (err)
  return err;
@@ -6715,6 +6733,12 @@ iwm_init(struct ifnet *ifp)
  ifq_clr_oactive(&ifp->if_snd);
  ifp->if_flags |= IFF_RUNNING;
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ return 0;
+ }
+
  ieee80211_begin_scan(ifp);
 
  /*
@@ -7194,37 +7218,104 @@ do { \
 #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
 
 void
-iwm_notif_intr(struct iwm_softc *sc)
+iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen)
 {
- uint16_t hw;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = IC2IFP(ic);
+ struct iwm_rx_packet *pkt;
+ struct iwm_rx_mpdu_res_start *rx_res;
+ uint16_t len;
+ uint32_t rx_pkt_status;
+ int rxfail;
 
- bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
-    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+ pkt = mtod(m, struct iwm_rx_packet *);
+ rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
+ len = le16toh(rx_res->byte_count);
+ if (len < IEEE80211_MIN_LEN) {
+ ic->ic_stats.is_rx_tooshort++;
+ IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
+ return;
+ }
+ if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen ||
+    len > IEEE80211_MAX_LEN) {
+ IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
+ return;
+ }
 
- hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
- hw &= (IWM_RX_RING_COUNT - 1);
- while (sc->rxq.cur != hw) {
- struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
- struct iwm_rx_packet *pkt;
- int qid, idx, code, handled = 1;
+ memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len,
+    sizeof(rx_pkt_status));
+ rx_pkt_status = le32toh(rx_pkt_status);
+ rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 ||
+    (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0);
+ if (rxfail) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ return;
+ }
 
- bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt),
-    BUS_DMASYNC_POSTREAD);
- pkt = mtod(data->m, struct iwm_rx_packet *);
+ /* Extract the 802.11 frame. */
+ m->m_data = (caddr_t)pkt->data + sizeof(*rx_res);
+ m->m_pkthdr.len = m->m_len = len;
+ if (iwm_rx_frame(sc, m, rx_pkt_status) != 0) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ }
+}
 
+int
+iwm_rx_pkt_valid(struct iwm_rx_packet *pkt)
+{
+ int qid, idx, code;
+
+ code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
+ qid = pkt->hdr.qid & ~0x80;
+ idx = pkt->hdr.idx;
+
+ if ((code == 0 && qid == 0 && idx == 0) ||
+    pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID))
+ return 0;
+
+ return 1;
+}
+
+void
+iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data)
+{
+ struct ifnet *ifp = IC2IFP(&sc->sc_ic);
+ struct iwm_rx_packet *pkt, *nextpkt;
+ uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
+ struct mbuf *m0, *m;
+ const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
+ size_t remain = IWM_RBUF_SIZE;
+ int qid, idx, code, handled = 1;
+
+ bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
+    BUS_DMASYNC_POSTREAD);
+
+ m0 = data->m;
+ while (m0 && offset + minsz < IWM_RBUF_SIZE) {
+ pkt = (struct iwm_rx_packet *)(m0->m_data + offset);
+
+ if (!iwm_rx_pkt_valid(pkt))
+ break;
+
+ code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
  qid = pkt->hdr.qid & ~0x80;
  idx = pkt->hdr.idx;
+ len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt);
+ if (len < sizeof(pkt->hdr) ||
+    len > (IWM_RBUF_SIZE - offset - minsz))
+ break;
 
- code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
-
- /*
- * randomly get these from the firmware, no idea why.
- * they at least seem harmless, so just ignore them for now
- */
- if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
-    || pkt->len_n_flags == htole32(0x55550000))) {
- ADVANCE_RXQ(sc);
- continue;
+ if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
+ /* Take mbuf m0 off the RX ring. */
+ if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) {
+ ifp->if_ierrors++;
+ break;
+ }
+ KASSERT(data->m != m0);
  }
 
  switch (code) {
@@ -7232,10 +7323,40 @@ iwm_notif_intr(struct iwm_softc *sc)
  iwm_rx_rx_phy_cmd(sc, pkt, data);
  break;
 
- case IWM_REPLY_RX_MPDU_CMD:
- iwm_rx_rx_mpdu(sc, pkt, data);
- break;
+ case IWM_REPLY_RX_MPDU_CMD: {
+ nextoff = offset +
+    roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+ nextpkt = (struct iwm_rx_packet *)
+    (m0->m_data + nextoff);
+ if (nextoff + minsz >= IWM_RBUF_SIZE ||
+    !iwm_rx_pkt_valid(nextpkt)) {
+ /* No need to copy last frame in buffer. */
+ if (offset > 0)
+ m_adj(m0, offset);
+ iwm_rx_mpdu(sc, m0, remain - minsz);
+ m0 = NULL; /* stack owns m0 now; abort loop */
+ } else {
+ /*
+ * Create an mbuf which points to the current
+ * packet. Always copy from offset zero to
+ * preserve m_pkthdr.
+ */
+ m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
+ if (m == NULL) {
+ ifp->if_ierrors++;
+ break;
+ }
+ m_adj(m, offset);
+ iwm_rx_mpdu(sc, m, remain - minsz);
+ }
 
+ if (offset + minsz < remain)
+ remain -= offset;
+ else
+ remain = minsz;
+ break;
+ }
+
  case IWM_TX_CMD:
  iwm_rx_tx_cmd(sc, pkt, data);
  break;
@@ -7479,6 +7600,26 @@ iwm_notif_intr(struct iwm_softc *sc)
  iwm_cmd_done(sc, pkt);
  }
 
+ offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+ }
+
+ if (m0 && m0 != data->m)
+ m_freem(m0);
+}
+
+void
+iwm_notif_intr(struct iwm_softc *sc)
+{
+ uint16_t hw;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
+    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+
+ hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
+ hw &= (IWM_RX_RING_COUNT - 1);
+ while (sc->rxq.cur != hw) {
+ struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
+ iwm_rx_pkt(sc, data);
  ADVANCE_RXQ(sc);
  }
 
@@ -7962,6 +8103,7 @@ iwm_attach(struct device *parent, struct device *self,
     IEEE80211_C_RSN | /* WPA/RSN */
     IEEE80211_C_SCANALL | /* device scans all channels at once */
     IEEE80211_C_SCANALLBAND | /* device scans all bands at once */
+    IEEE80211_C_MONITOR | /* monitor mode supported */
     IEEE80211_C_SHSLOT | /* short slot time supported */
     IEEE80211_C_SHPREAMBLE; /* short preamble supported */
 

Reply | Threaded
Open this post in threaded view
|

Re: once again: iwm(4) multi-frame rx + monitor mode

Bruno Flueckiger
On 30.08., Stefan Sperling wrote:

> I would like to try this again: In iwm(4), process more than one frame
> per Rx interrupt, and enable monitor mode.
>
> Monitor mode triggers "unhandled fimware response" errors without multi-Rx
> support. We have seen these infamous errors in other contexts as well.
> It is possible that the firmware decides to use multi-Rx in such cases
> which would of course confuse our driver.
>
> The fact is that Linux has not used single-Rx mode for years. One of their
> developers told me that Intel has stopped testing it entirely. The current
> code isn't future-proof and is likely to break with newer versions of
> firmware and/or hardware.
>
> These changes were attempted and reverted twice already.
> Last time because they were causing throughput issues (and I now see why).
> I have rebased my old diff and tweaked it slightly to avoid an unneeded
> mbuf copy and reworked all the offset/length calculations once again.
>
> Works fine here against several APs with no observable drop in throughput.
>
> Please test this on as many iwm devices as possible. Thanks!
>

Your diff is working on my Intel Dual Band Wireless AC 3165 for three
days now. I haven't encounter any problems so far. Please tell me if you
need specific tests or more information about my setup.

Cheers,
Bruno

Reply | Threaded
Open this post in threaded view
|

Re: once again: iwm(4) multi-frame rx + monitor mode

Richard Procter
In reply to this post by Stefan Sperling-5
On Fri, 30 Aug 2019, Stefan Sperling wrote:

> I would like to try this again: In iwm(4), process more than one frame
> per Rx interrupt, and enable monitor mode.
>
> Monitor mode triggers "unhandled fimware response" errors without multi-Rx
> support. We have seen these infamous errors in other contexts as well.
> It is possible that the firmware decides to use multi-Rx in such cases
> which would of course confuse our driver.
> [...]
> Please test this on as many iwm devices as possible. Thanks!

I've tested this some. I used nc(1) to transfer a 500MB file between
iwm(4) and my ral(4) AP once in each direction, both with and without the
patch; diff'd the netstat -s output; and tried the one of the most
demanding tests I know for an interface, which is:

AP# ping -q -s 10 -l 400 -c 400 -f iwm_machine

I've seen no problems or regressions. The patched transfer saw ~1/3 of the
duplicate packets received but this could easily be noise. Every ping was
received, according to netstat.

A big caveat is that my ral(4) AP is 11g and unlikely to be pushing iwm(4)
hard. I forced ral(4) to OFDM54, and iwm(4) was seeing 73% signal strength
from it.

I've not used monitor mode before. I was expecting to see frames from
other nodes in tcpdump with monitor mode enabled, but didn't, whether
iwm(4) was associated with a network or not. I didn't see any "unhandled
firmware response" errors.

best,
Richard.

iwm0 at pci1 dev 0 function 0 "Intel Dual Band Wireless AC 7265" rev 0x69, msi
iwm0: hw rev 0x210, fw ver 16.242414.0, address xx:xx:xx:xx:xx:xx

>
> diff refs/heads/master refs/heads/iwm-multirx
> blob - 42cb00c62cc455d871da09560dcf82876db2f4c0
> blob + c9a4dc36c3a7cf09c9836141b6a966370ac94824
> --- sys/dev/pci/if_iwm.c
> +++ sys/dev/pci/if_iwm.c
> @@ -367,6 +367,7 @@ int iwm_get_signal_strength(struct iwm_softc *, struct
>  void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *,
>      struct iwm_rx_data *);
>  int iwm_get_noise(const struct iwm_statistics_rx_non_phy *);
> +int iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t);
>  int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
>      struct ieee80211_node *);
>  void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
> @@ -431,7 +432,7 @@ uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int)
>  int iwm_rval2ridx(int);
>  void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
>  void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *,
> -    struct iwm_mac_ctx_cmd *, uint32_t, int);
> +    struct iwm_mac_ctx_cmd *, uint32_t);
>  void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
>      struct iwm_mac_data_sta *, int);
>  int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
> @@ -476,6 +477,9 @@ const char *iwm_desc_lookup(uint32_t);
>  void iwm_nic_error(struct iwm_softc *);
>  void iwm_nic_umac_error(struct iwm_softc *);
>  #endif
> +void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t);
> +int iwm_rx_pkt_valid(struct iwm_rx_packet *);
> +void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *);
>  void iwm_notif_intr(struct iwm_softc *);
>  int iwm_intr(void *);
>  int iwm_match(struct device *, void *, void *);
> @@ -1729,7 +1733,6 @@ iwm_nic_rx_init(struct iwm_softc *sc)
>      IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
>      IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |  /* HW bug */
>      IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
> -    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
>      (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
>      IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
>      IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
> @@ -3514,57 +3517,23 @@ iwm_ccmp_decap(struct iwm_softc *sc, struct mbuf *m, s
>   return 0;
>  }
>  
> -void
> -iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> -    struct iwm_rx_data *data)
> +int
> +iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status)
>  {
>   struct ieee80211com *ic = &sc->sc_ic;
> - struct ifnet *ifp = IC2IFP(ic);
>   struct ieee80211_frame *wh;
>   struct ieee80211_node *ni;
>   struct ieee80211_rxinfo rxi;
>   struct ieee80211_channel *bss_chan;
> - struct mbuf *m;
>   struct iwm_rx_phy_info *phy_info;
> - struct iwm_rx_mpdu_res_start *rx_res;
>   int device_timestamp;
> - uint32_t len;
> - uint32_t rx_pkt_status;
>   int rssi, chanidx;
>   uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
>  
> - bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
> -    BUS_DMASYNC_POSTREAD);
> -
>   phy_info = &sc->sc_last_phy_info;
> - rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
> - wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
> - len = le16toh(rx_res->byte_count);
> - if (len < IEEE80211_MIN_LEN) {
> - ic->ic_stats.is_rx_tooshort++;
> - IC2IFP(ic)->if_ierrors++;
> - return;
> - }
> - if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) {
> - IC2IFP(ic)->if_ierrors++;
> - return;
> - }
> - rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
> -    sizeof(*rx_res) + len));
> -
>   if (__predict_false(phy_info->cfg_phy_cnt > 20))
> - return;
> + return EINVAL;
>  
> - if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
> -    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
> - return; /* drop */
> -
> - m = data->m;
> - if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
> - return;
> - m->m_data = pkt->data + sizeof(*rx_res);
> - m->m_pkthdr.len = m->m_len = len;
> -
>   device_timestamp = le32toh(phy_info->system_timestamp);
>  
>   if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
> @@ -3579,6 +3548,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
>   if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))
>   chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
>  
> + wh = mtod(m, struct ieee80211_frame *);
>   ni = ieee80211_find_rxnode(ic, wh);
>   if (ni == ic->ic_bss) {
>   /*
> @@ -3603,7 +3573,6 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
>   if ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK) !=
>      IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
>   ic->ic_stats.is_ccmp_dec_errs++;
> - ifp->if_ierrors++;
>   m_freem(m);
>   goto done;
>   }
> @@ -3614,12 +3583,10 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
>      (IWM_RX_MPDU_RES_STATUS_DEC_DONE |
>      IWM_RX_MPDU_RES_STATUS_MIC_OK)) {
>   ic->ic_stats.is_ccmp_dec_errs++;
> - ifp->if_ierrors++;
>   m_freem(m);
>   goto done;
>   }
>   if (iwm_ccmp_decap(sc, m, ni) != 0) {
> - ifp->if_ierrors++;
>   m_freem(m);
>   goto done;
>   }
> @@ -3691,6 +3658,8 @@ done:
>   if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
>   ni->ni_chan = bss_chan;
>   ieee80211_release_node(ic, ni);
> +
> + return 0;
>  }
>  
>  void
> @@ -4666,6 +4635,7 @@ void
>  iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in,
>      struct iwm_mac_power_cmd *cmd)
>  {
> + struct ieee80211com *ic = &sc->sc_ic;
>   struct ieee80211_node *ni = &in->in_ni;
>   int dtim_period, dtim_msec, keep_alive;
>  
> @@ -4687,7 +4657,8 @@ iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_n
>   keep_alive = roundup(keep_alive, 1000) / 1000;
>   cmd->keep_alive_seconds = htole16(keep_alive);
>  
> - cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
> + if (ic->ic_opmode != IEEE80211_M_MONITOR)
> + cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
>  }
>  
>  int
> @@ -4714,13 +4685,15 @@ iwm_power_mac_update_mode(struct iwm_softc *sc, struct
>  int
>  iwm_power_update_device(struct iwm_softc *sc)
>  {
> - struct iwm_device_power_cmd cmd = {
> - .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
> - };
> + struct iwm_device_power_cmd cmd = { };
> + struct ieee80211com *ic = &sc->sc_ic;
>  
>   if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
>   return 0;
>  
> + if (ic->ic_opmode != IEEE80211_M_MONITOR)
> + cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
> +
>   return iwm_send_cmd_pdu(sc,
>      IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
>  }
> @@ -4782,7 +4755,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node
>   add_sta_cmd.tfd_queue_msk |=
>      htole32(1 << iwm_ac_to_tx_fifo[ac]);
>   }
> - IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> +    etherbroadcastaddr);
> + else
> + IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> +    in->in_ni.ni_bssid);
>   }
>   add_sta_cmd.add_modify = update ? 1 : 0;
>   add_sta_cmd.station_flags_msk
> @@ -5452,7 +5430,7 @@ iwm_ack_rates(struct iwm_softc *sc, struct iwm_node *i
>  
>  void
>  iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
> -    struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc)
> +    struct iwm_mac_ctx_cmd *cmd, uint32_t action)
>  {
>  #define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
>   struct ieee80211com *ic = &sc->sc_ic;
> @@ -5464,12 +5442,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct i
>      in->in_color));
>   cmd->action = htole32(action);
>  
> - cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER);
> + else if (ic->ic_opmode == IEEE80211_M_STA)
> + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
> + else
> + panic("unsupported operating mode %d\n", ic->ic_opmode);
>   cmd->tsf_id = htole32(IWM_TSF_ID_A);
>  
>   IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr);
> - IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
> + return;
> + }
>  
> + IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
>   iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
>   cmd->cck_rates = htole32(cck_ack_rates);
>   cmd->ofdm_rates = htole32(ofdm_ack_rates);
> @@ -5560,6 +5547,7 @@ int
>  iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action,
>      int assoc)
>  {
> + struct ieee80211com *ic = &sc->sc_ic;
>   struct ieee80211_node *ni = &in->in_ni;
>   struct iwm_mac_ctx_cmd cmd;
>   int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE);
> @@ -5571,11 +5559,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node
>  
>   memset(&cmd, 0, sizeof(cmd));
>  
> - iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc);
> + iwm_mac_ctxt_cmd_common(sc, in, &cmd, action);
>  
> - /* Allow beacons to pass through as long as we are not associated or we
> - * do not have dtim period information */
> - if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC |
> +    IWM_MAC_FILTER_IN_CONTROL_AND_MGMT |
> +    IWM_MAC_FILTER_IN_BEACON |
> +    IWM_MAC_FILTER_IN_PROBE_REQUEST |
> +    IWM_MAC_FILTER_IN_CRC32);
> + } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
> + /*
> + * Allow beacons to pass through as long as we are not
> + * associated or we do not have dtim period information.
> + */
>   cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
>   else
>   iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
> @@ -5792,7 +5788,10 @@ iwm_auth(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> - sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
> + else
> + sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
>   err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
>      IWM_FW_CTXT_ACTION_MODIFY, 0);
>   if (err) {
> @@ -5826,6 +5825,9 @@ iwm_auth(struct iwm_softc *sc)
>   }
>   sc->sc_flags |= IWM_FLAG_STA_ACTIVE;
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + return 0;
> +
>   /*
>   * Prevent the FW from wandering off channel during association
>   * by "protecting" the session with a time event.
> @@ -5956,8 +5958,16 @@ iwm_run(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + /* Add a MAC context and a sniffing STA. */
> + err = iwm_auth(sc);
> + if (err)
> + return err;
> + }
> +
>   /* Configure Rx chains for MIMO. */
> - if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
> + if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
> +    (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
>      !sc->sc_nvm.sku_cap_mimo_disable) {
>   err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
>      2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0);
> @@ -6025,6 +6035,11 @@ iwm_run(struct iwm_softc *sc)
>   ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
>   ieee80211_mira_node_init(&in->in_mn);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + iwm_led_blink_start(sc);
> + return 0;
> + }
> +
>   /* Start at lowest available bit-rate, AMRR will raise. */
>   in->in_ni.ni_txrate = 0;
>   in->in_ni.ni_txmcs = 0;
> @@ -6044,6 +6059,9 @@ iwm_run_stop(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + iwm_led_blink_stop(sc);
> +
>   err = iwm_sf_config(sc, IWM_SF_INIT_OFF);
>   if (err)
>   return err;
> @@ -6715,6 +6733,12 @@ iwm_init(struct ifnet *ifp)
>   ifq_clr_oactive(&ifp->if_snd);
>   ifp->if_flags |= IFF_RUNNING;
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + ic->ic_bss->ni_chan = ic->ic_ibss_chan;
> + ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
> + return 0;
> + }
> +
>   ieee80211_begin_scan(ifp);
>  
>   /*
> @@ -7194,37 +7218,104 @@ do { \
>  #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
>  
>  void
> -iwm_notif_intr(struct iwm_softc *sc)
> +iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen)
>  {
> - uint16_t hw;
> + struct ieee80211com *ic = &sc->sc_ic;
> + struct ifnet *ifp = IC2IFP(ic);
> + struct iwm_rx_packet *pkt;
> + struct iwm_rx_mpdu_res_start *rx_res;
> + uint16_t len;
> + uint32_t rx_pkt_status;
> + int rxfail;
>  
> - bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
> -    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
> + pkt = mtod(m, struct iwm_rx_packet *);
> + rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
> + len = le16toh(rx_res->byte_count);
> + if (len < IEEE80211_MIN_LEN) {
> + ic->ic_stats.is_rx_tooshort++;
> + IC2IFP(ic)->if_ierrors++;
> + m_freem(m);
> + return;
> + }
> + if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen ||
> +    len > IEEE80211_MAX_LEN) {
> + IC2IFP(ic)->if_ierrors++;
> + m_freem(m);
> + return;
> + }
>  
> - hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
> - hw &= (IWM_RX_RING_COUNT - 1);
> - while (sc->rxq.cur != hw) {
> - struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
> - struct iwm_rx_packet *pkt;
> - int qid, idx, code, handled = 1;
> + memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len,
> +    sizeof(rx_pkt_status));
> + rx_pkt_status = le32toh(rx_pkt_status);
> + rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 ||
> +    (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0);
> + if (rxfail) {
> + ifp->if_ierrors++;
> + m_freem(m);
> + return;
> + }
>  
> - bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt),
> -    BUS_DMASYNC_POSTREAD);
> - pkt = mtod(data->m, struct iwm_rx_packet *);
> + /* Extract the 802.11 frame. */
> + m->m_data = (caddr_t)pkt->data + sizeof(*rx_res);
> + m->m_pkthdr.len = m->m_len = len;
> + if (iwm_rx_frame(sc, m, rx_pkt_status) != 0) {
> + ifp->if_ierrors++;
> + m_freem(m);
> + }
> +}
>  
> +int
> +iwm_rx_pkt_valid(struct iwm_rx_packet *pkt)
> +{
> + int qid, idx, code;
> +
> + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
> + qid = pkt->hdr.qid & ~0x80;
> + idx = pkt->hdr.idx;
> +
> + if ((code == 0 && qid == 0 && idx == 0) ||
> +    pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID))
> + return 0;
> +
> + return 1;
> +}
> +
> +void
> +iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data)
> +{
> + struct ifnet *ifp = IC2IFP(&sc->sc_ic);
> + struct iwm_rx_packet *pkt, *nextpkt;
> + uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
> + struct mbuf *m0, *m;
> + const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
> + size_t remain = IWM_RBUF_SIZE;
> + int qid, idx, code, handled = 1;
> +
> + bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
> +    BUS_DMASYNC_POSTREAD);
> +
> + m0 = data->m;
> + while (m0 && offset + minsz < IWM_RBUF_SIZE) {
> + pkt = (struct iwm_rx_packet *)(m0->m_data + offset);
> +
> + if (!iwm_rx_pkt_valid(pkt))
> + break;
> +
> + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
>   qid = pkt->hdr.qid & ~0x80;
>   idx = pkt->hdr.idx;
> + len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt);
> + if (len < sizeof(pkt->hdr) ||
> +    len > (IWM_RBUF_SIZE - offset - minsz))
> + break;
>  
> - code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
> -
> - /*
> - * randomly get these from the firmware, no idea why.
> - * they at least seem harmless, so just ignore them for now
> - */
> - if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
> -    || pkt->len_n_flags == htole32(0x55550000))) {
> - ADVANCE_RXQ(sc);
> - continue;
> + if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
> + /* Take mbuf m0 off the RX ring. */
> + if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) {
> + ifp->if_ierrors++;
> + break;
> + }
> + KASSERT(data->m != m0);
>   }
>  
>   switch (code) {
> @@ -7232,10 +7323,40 @@ iwm_notif_intr(struct iwm_softc *sc)
>   iwm_rx_rx_phy_cmd(sc, pkt, data);
>   break;
>  
> - case IWM_REPLY_RX_MPDU_CMD:
> - iwm_rx_rx_mpdu(sc, pkt, data);
> - break;
> + case IWM_REPLY_RX_MPDU_CMD: {
> + nextoff = offset +
> +    roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
> + nextpkt = (struct iwm_rx_packet *)
> +    (m0->m_data + nextoff);
> + if (nextoff + minsz >= IWM_RBUF_SIZE ||
> +    !iwm_rx_pkt_valid(nextpkt)) {
> + /* No need to copy last frame in buffer. */
> + if (offset > 0)
> + m_adj(m0, offset);
> + iwm_rx_mpdu(sc, m0, remain - minsz);
> + m0 = NULL; /* stack owns m0 now; abort loop */
> + } else {
> + /*
> + * Create an mbuf which points to the current
> + * packet. Always copy from offset zero to
> + * preserve m_pkthdr.
> + */
> + m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
> + if (m == NULL) {
> + ifp->if_ierrors++;
> + break;
> + }
> + m_adj(m, offset);
> + iwm_rx_mpdu(sc, m, remain - minsz);
> + }
>  
> + if (offset + minsz < remain)
> + remain -= offset;
> + else
> + remain = minsz;
> + break;
> + }
> +
>   case IWM_TX_CMD:
>   iwm_rx_tx_cmd(sc, pkt, data);
>   break;
> @@ -7479,6 +7600,26 @@ iwm_notif_intr(struct iwm_softc *sc)
>   iwm_cmd_done(sc, pkt);
>   }
>  
> + offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
> + }
> +
> + if (m0 && m0 != data->m)
> + m_freem(m0);
> +}
> +
> +void
> +iwm_notif_intr(struct iwm_softc *sc)
> +{
> + uint16_t hw;
> +
> + bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
> +    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
> +
> + hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
> + hw &= (IWM_RX_RING_COUNT - 1);
> + while (sc->rxq.cur != hw) {
> + struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
> + iwm_rx_pkt(sc, data);
>   ADVANCE_RXQ(sc);
>   }
>  
> @@ -7962,6 +8103,7 @@ iwm_attach(struct device *parent, struct device *self,
>      IEEE80211_C_RSN | /* WPA/RSN */
>      IEEE80211_C_SCANALL | /* device scans all channels at once */
>      IEEE80211_C_SCANALLBAND | /* device scans all bands at once */
> +    IEEE80211_C_MONITOR | /* monitor mode supported */
>      IEEE80211_C_SHSLOT | /* short slot time supported */
>      IEEE80211_C_SHPREAMBLE; /* short preamble supported */
>  
>
>

Reply | Threaded
Open this post in threaded view
|

Re: once again: iwm(4) multi-frame rx + monitor mode

Stefan Sperling-5
In reply to this post by Stefan Sperling-5
On Fri, Aug 30, 2019 at 03:59:08PM +0200, Stefan Sperling wrote:

> I would like to try this again: In iwm(4), process more than one frame
> per Rx interrupt, and enable monitor mode.
>
> Monitor mode triggers "unhandled fimware response" errors without multi-Rx
> support. We have seen these infamous errors in other contexts as well.
> It is possible that the firmware decides to use multi-Rx in such cases
> which would of course confuse our driver.
>
> The fact is that Linux has not used single-Rx mode for years. One of their
> developers told me that Intel has stopped testing it entirely. The current
> code isn't future-proof and is likely to break with newer versions of
> firmware and/or hardware.

Updated diff, rebased on top of -current

If you were seeing throughput problems when testing this diff in the
previous round, please try again.

I suspect such problems were due to the ifq drop issue which has since
been fixed in -current. The diff below results in more packets per interrupt
being delivered to the network stack which is exactly what made ifq drops
more likely to occur. This should no longer be an issue.

I have tested this diff on 8265 and 7260 and I don't see any problems.

diff refs/heads/master refs/heads/iwm-multirx
blob - 20ce2cbe3d2c2994b9a498965a3c9f29a5f31c9c
blob + d3baf8473bd86beb05eaed7eb6cb715f90790c46
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -367,8 +367,10 @@ int iwm_get_signal_strength(struct iwm_softc *, struct
 void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *,
     struct iwm_rx_data *);
 int iwm_get_noise(const struct iwm_statistics_rx_non_phy *);
-void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
-    struct iwm_rx_data *, struct mbuf_list *);
+int iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t,
+    struct mbuf_list *);
+int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
+    struct ieee80211_node *);
 void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *);
 void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *,
     struct iwm_node *);
@@ -429,7 +431,7 @@ uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int)
 int iwm_rval2ridx(int);
 void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
 void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *,
-    struct iwm_mac_ctx_cmd *, uint32_t, int);
+    struct iwm_mac_ctx_cmd *, uint32_t);
 void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
     struct iwm_mac_data_sta *, int);
 int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
@@ -470,6 +472,11 @@ const char *iwm_desc_lookup(uint32_t);
 void iwm_nic_error(struct iwm_softc *);
 void iwm_nic_umac_error(struct iwm_softc *);
 #endif
+void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t,
+    struct mbuf_list *);
+int iwm_rx_pkt_valid(struct iwm_rx_packet *);
+void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *,
+    struct mbuf_list *);
 void iwm_notif_intr(struct iwm_softc *);
 int iwm_intr(void *);
 int iwm_match(struct device *, void *, void *);
@@ -1723,7 +1730,6 @@ iwm_nic_rx_init(struct iwm_softc *sc)
     IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
     IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |  /* HW bug */
     IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
-    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
     (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
     IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
     IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
@@ -3429,56 +3435,24 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy *
  return (nbant == 0) ? -127 : (total / nbant) - 107;
 }
 
-void
-iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
-    struct iwm_rx_data *data, struct mbuf_list *ml)
+int
+iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status,
+    struct mbuf_list *ml)
 {
  struct ieee80211com *ic = &sc->sc_ic;
  struct ieee80211_frame *wh;
  struct ieee80211_node *ni;
  struct ieee80211_rxinfo rxi;
  struct ieee80211_channel *bss_chan;
- struct mbuf *m;
  struct iwm_rx_phy_info *phy_info;
- struct iwm_rx_mpdu_res_start *rx_res;
  int device_timestamp;
- uint32_t len;
- uint32_t rx_pkt_status;
  int rssi, chanidx;
  uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
 
- bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
-    BUS_DMASYNC_POSTREAD);
-
  phy_info = &sc->sc_last_phy_info;
- rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
- wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
- len = le16toh(rx_res->byte_count);
- if (len < IEEE80211_MIN_LEN) {
- ic->ic_stats.is_rx_tooshort++;
- IC2IFP(ic)->if_ierrors++;
- return;
- }
- if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) {
- IC2IFP(ic)->if_ierrors++;
- return;
- }
- rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
-    sizeof(*rx_res) + len));
-
  if (__predict_false(phy_info->cfg_phy_cnt > 20))
- return;
+ return EINVAL;
 
- if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
-    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
- return; /* drop */
-
- m = data->m;
- if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
- return;
- m->m_data = pkt->data + sizeof(*rx_res);
- m->m_pkthdr.len = m->m_len = len;
-
  device_timestamp = le32toh(phy_info->system_timestamp);
 
  if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
@@ -3493,6 +3467,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
  if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))
  chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
 
+ wh = mtod(m, struct ieee80211_frame *);
  ni = ieee80211_find_rxnode(ic, wh);
  if (ni == ic->ic_bss) {
  /*
@@ -3572,6 +3547,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
  if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
  ni->ni_chan = bss_chan;
  ieee80211_release_node(ic, ni);
+
+ return 0;
 }
 
 void
@@ -4521,6 +4498,7 @@ void
 iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in,
     struct iwm_mac_power_cmd *cmd)
 {
+ struct ieee80211com *ic = &sc->sc_ic;
  struct ieee80211_node *ni = &in->in_ni;
  int dtim_period, dtim_msec, keep_alive;
 
@@ -4542,7 +4520,8 @@ iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_n
  keep_alive = roundup(keep_alive, 1000) / 1000;
  cmd->keep_alive_seconds = htole16(keep_alive);
 
- cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+ if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
 }
 
 int
@@ -4569,13 +4548,15 @@ iwm_power_mac_update_mode(struct iwm_softc *sc, struct
 int
 iwm_power_update_device(struct iwm_softc *sc)
 {
- struct iwm_device_power_cmd cmd = {
- .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
- };
+ struct iwm_device_power_cmd cmd = { };
+ struct ieee80211com *ic = &sc->sc_ic;
 
  if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
  return 0;
 
+ if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
  return iwm_send_cmd_pdu(sc,
     IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
 }
@@ -4637,7 +4618,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node
  add_sta_cmd.tfd_queue_msk |=
     htole32(1 << iwm_ac_to_tx_fifo[ac]);
  }
- IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
+    etherbroadcastaddr);
+ else
+ IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
+    in->in_ni.ni_bssid);
  }
  add_sta_cmd.add_modify = update ? 1 : 0;
  add_sta_cmd.station_flags_msk
@@ -5307,7 +5293,7 @@ iwm_ack_rates(struct iwm_softc *sc, struct iwm_node *i
 
 void
 iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
-    struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc)
+    struct iwm_mac_ctx_cmd *cmd, uint32_t action)
 {
 #define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
  struct ieee80211com *ic = &sc->sc_ic;
@@ -5319,12 +5305,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct i
     in->in_color));
  cmd->action = htole32(action);
 
- cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER);
+ else if (ic->ic_opmode == IEEE80211_M_STA)
+ cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+ else
+ panic("unsupported operating mode %d\n", ic->ic_opmode);
  cmd->tsf_id = htole32(IWM_TSF_ID_A);
 
  IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr);
- IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
+ return;
+ }
 
+ IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
  iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
  cmd->cck_rates = htole32(cck_ack_rates);
  cmd->ofdm_rates = htole32(ofdm_ack_rates);
@@ -5415,6 +5410,7 @@ int
 iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action,
     int assoc)
 {
+ struct ieee80211com *ic = &sc->sc_ic;
  struct ieee80211_node *ni = &in->in_ni;
  struct iwm_mac_ctx_cmd cmd;
  int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE);
@@ -5426,11 +5422,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node
 
  memset(&cmd, 0, sizeof(cmd));
 
- iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc);
+ iwm_mac_ctxt_cmd_common(sc, in, &cmd, action);
 
- /* Allow beacons to pass through as long as we are not associated or we
- * do not have dtim period information */
- if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC |
+    IWM_MAC_FILTER_IN_CONTROL_AND_MGMT |
+    IWM_MAC_FILTER_IN_BEACON |
+    IWM_MAC_FILTER_IN_PROBE_REQUEST |
+    IWM_MAC_FILTER_IN_CRC32);
+ } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
+ /*
+ * Allow beacons to pass through as long as we are not
+ * associated or we do not have dtim period information.
+ */
  cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
  else
  iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
@@ -5654,7 +5658,10 @@ iwm_auth(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
- sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
+ else
+ sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
  err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
     IWM_FW_CTXT_ACTION_MODIFY, 0);
  if (err) {
@@ -5688,6 +5695,9 @@ iwm_auth(struct iwm_softc *sc)
  }
  sc->sc_flags |= IWM_FLAG_STA_ACTIVE;
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ return 0;
+
  /*
  * Prevent the FW from wandering off channel during association
  * by "protecting" the session with a time event.
@@ -5818,8 +5828,16 @@ iwm_run(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ /* Add a MAC context and a sniffing STA. */
+ err = iwm_auth(sc);
+ if (err)
+ return err;
+ }
+
  /* Configure Rx chains for MIMO. */
- if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
+ if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
+    (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
     !sc->sc_nvm.sku_cap_mimo_disable) {
  err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
     2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0);
@@ -5887,6 +5905,11 @@ iwm_run(struct iwm_softc *sc)
  ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
  ieee80211_mira_node_init(&in->in_mn);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ iwm_led_blink_start(sc);
+ return 0;
+ }
+
  /* Start at lowest available bit-rate, AMRR will raise. */
  in->in_ni.ni_txrate = 0;
  in->in_ni.ni_txmcs = 0;
@@ -5906,6 +5929,9 @@ iwm_run_stop(struct iwm_softc *sc)
 
  splassert(IPL_NET);
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ iwm_led_blink_stop(sc);
+
  err = iwm_sf_config(sc, IWM_SF_INIT_OFF);
  if (err)
  return err;
@@ -6524,6 +6550,12 @@ iwm_init(struct ifnet *ifp)
  ifq_clr_oactive(&ifp->if_snd);
  ifp->if_flags |= IFF_RUNNING;
 
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ return 0;
+ }
+
  ieee80211_begin_scan(ifp);
 
  /*
@@ -7003,38 +7035,105 @@ do { \
 #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
 
 void
-iwm_notif_intr(struct iwm_softc *sc)
+iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen,
+    struct mbuf_list *ml)
 {
- struct mbuf_list ml = MBUF_LIST_INITIALIZER();
- uint16_t hw;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = IC2IFP(ic);
+ struct iwm_rx_packet *pkt;
+ struct iwm_rx_mpdu_res_start *rx_res;
+ uint16_t len;
+ uint32_t rx_pkt_status;
+ int rxfail;
 
- bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
-    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+ pkt = mtod(m, struct iwm_rx_packet *);
+ rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
+ len = le16toh(rx_res->byte_count);
+ if (len < IEEE80211_MIN_LEN) {
+ ic->ic_stats.is_rx_tooshort++;
+ IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
+ return;
+ }
+ if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen ||
+    len > IEEE80211_MAX_LEN) {
+ IC2IFP(ic)->if_ierrors++;
+ m_freem(m);
+ return;
+ }
 
- hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
- hw &= (IWM_RX_RING_COUNT - 1);
- while (sc->rxq.cur != hw) {
- struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
- struct iwm_rx_packet *pkt;
- int qid, idx, code, handled = 1;
+ memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len,
+    sizeof(rx_pkt_status));
+ rx_pkt_status = le32toh(rx_pkt_status);
+ rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 ||
+    (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0);
+ if (rxfail) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ return;
+ }
 
- bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt),
-    BUS_DMASYNC_POSTREAD);
- pkt = mtod(data->m, struct iwm_rx_packet *);
+ /* Extract the 802.11 frame. */
+ m->m_data = (caddr_t)pkt->data + sizeof(*rx_res);
+ m->m_pkthdr.len = m->m_len = len;
+ if (iwm_rx_frame(sc, m, rx_pkt_status, ml) != 0) {
+ ifp->if_ierrors++;
+ m_freem(m);
+ }
+}
 
+int
+iwm_rx_pkt_valid(struct iwm_rx_packet *pkt)
+{
+ int qid, idx, code;
+
+ code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
+ qid = pkt->hdr.qid & ~0x80;
+ idx = pkt->hdr.idx;
+
+ if ((code == 0 && qid == 0 && idx == 0) ||
+    pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID))
+ return 0;
+
+ return 1;
+}
+
+void
+iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data, struct mbuf_list *ml)
+{
+ struct ifnet *ifp = IC2IFP(&sc->sc_ic);
+ struct iwm_rx_packet *pkt, *nextpkt;
+ uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
+ struct mbuf *m0, *m;
+ const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
+ size_t remain = IWM_RBUF_SIZE;
+ int qid, idx, code, handled = 1;
+
+ bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
+    BUS_DMASYNC_POSTREAD);
+
+ m0 = data->m;
+ while (m0 && offset + minsz < IWM_RBUF_SIZE) {
+ pkt = (struct iwm_rx_packet *)(m0->m_data + offset);
+
+ if (!iwm_rx_pkt_valid(pkt))
+ break;
+
+ code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
  qid = pkt->hdr.qid & ~0x80;
  idx = pkt->hdr.idx;
+ len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt);
+ if (len < sizeof(pkt->hdr) ||
+    len > (IWM_RBUF_SIZE - offset - minsz))
+ break;
 
- code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
-
- /*
- * randomly get these from the firmware, no idea why.
- * they at least seem harmless, so just ignore them for now
- */
- if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
-    || pkt->len_n_flags == htole32(0x55550000))) {
- ADVANCE_RXQ(sc);
- continue;
+ if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
+ /* Take mbuf m0 off the RX ring. */
+ if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) {
+ ifp->if_ierrors++;
+ break;
+ }
+ KASSERT(data->m != m0);
  }
 
  switch (code) {
@@ -7042,10 +7141,40 @@ iwm_notif_intr(struct iwm_softc *sc)
  iwm_rx_rx_phy_cmd(sc, pkt, data);
  break;
 
- case IWM_REPLY_RX_MPDU_CMD:
- iwm_rx_rx_mpdu(sc, pkt, data, &ml);
- break;
+ case IWM_REPLY_RX_MPDU_CMD: {
+ nextoff = offset +
+    roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+ nextpkt = (struct iwm_rx_packet *)
+    (m0->m_data + nextoff);
+ if (nextoff + minsz >= IWM_RBUF_SIZE ||
+    !iwm_rx_pkt_valid(nextpkt)) {
+ /* No need to copy last frame in buffer. */
+ if (offset > 0)
+ m_adj(m0, offset);
+ iwm_rx_mpdu(sc, m0, remain - minsz, ml);
+ m0 = NULL; /* stack owns m0 now; abort loop */
+ } else {
+ /*
+ * Create an mbuf which points to the current
+ * packet. Always copy from offset zero to
+ * preserve m_pkthdr.
+ */
+ m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
+ if (m == NULL) {
+ ifp->if_ierrors++;
+ break;
+ }
+ m_adj(m, offset);
+ iwm_rx_mpdu(sc, m, remain - minsz, ml);
+ }
 
+ if (offset + minsz < remain)
+ remain -= offset;
+ else
+ remain = minsz;
+ break;
+ }
+
  case IWM_TX_CMD:
  iwm_rx_tx_cmd(sc, pkt, data);
  break;
@@ -7288,6 +7417,27 @@ iwm_notif_intr(struct iwm_softc *sc)
  iwm_cmd_done(sc, pkt);
  }
 
+ offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
+ }
+
+ if (m0 && m0 != data->m)
+ m_freem(m0);
+}
+
+void
+iwm_notif_intr(struct iwm_softc *sc)
+{
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+ uint16_t hw;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
+    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
+
+ hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
+ hw &= (IWM_RX_RING_COUNT - 1);
+ while (sc->rxq.cur != hw) {
+ struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
+ iwm_rx_pkt(sc, data, &ml);
  ADVANCE_RXQ(sc);
  }
  if_input(&sc->sc_ic.ic_if, &ml);
@@ -7772,6 +7922,7 @@ iwm_attach(struct device *parent, struct device *self,
     IEEE80211_C_RSN | /* WPA/RSN */
     IEEE80211_C_SCANALL | /* device scans all channels at once */
     IEEE80211_C_SCANALLBAND | /* device scans all bands at once */
+    IEEE80211_C_MONITOR | /* monitor mode supported */
     IEEE80211_C_SHSLOT | /* short slot time supported */
     IEEE80211_C_SHPREAMBLE; /* short preamble supported */
 

Reply | Threaded
Open this post in threaded view
|

Re: once again: iwm(4) multi-frame rx + monitor mode

Mikolaj Kucharski-3
Hi Stefan,

On Fri, Sep 13, 2019 at 02:10:12PM +0200, Stefan Sperling wrote:

> Updated diff, rebased on top of -current
>
> If you were seeing throughput problems when testing this diff in the
> previous round, please try again.
>
> I suspect such problems were due to the ifq drop issue which has since
> been fixed in -current. The diff below results in more packets per interrupt
> being delivered to the network stack which is exactly what made ifq drops
> more likely to occur. This should no longer be an issue.
>
> I have tested this diff on 8265 and 7260 and I don't see any problems.

iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless-AC 8265" rev 0x78, msi
iwm0: hw rev 0x230, fw ver 22.361476.0, address 38:37:8b:XX:XX:XX

I've tested monitor mode and it seems to work:

$ ifconfig iwm0
iwm0: flags=8847<UP,BROADCAST,DEBUG,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        lladdr 38:37:8b:XX:XX:XX
        index 1 priority 4 llprio 3
        groups: wlan egress
        media: IEEE802.11 autoselect monitor
        status: active
        ieee80211: nwid linksys chan 11 bssid 00:1d:7e:XX:XX:XX 29%

I've used kismet-201607R1p0 package and I could see networks and packets
being reported. Testing with tcpdump(8) also showed traffic visible from
various access points around:

# tcpdump -c 3 -y IEEE802_11_RADIO -ni iwm0  
tcpdump: listening on iwm0, link-type IEEE802_11_RADIO
21:25:12.586606 802.11: beacon, ssid (net_092382), rates, ds, tim, xrates, rsn, htcaps, <radiotap v0, chan 11, 11g, sig 50dBm, noise 28dBm>
21:25:12.653411 802.11: beacon, ssid (linksys), rates, ds, tim, erp, 47:1, xrates, vendor, <radiotap v0, chan 11, 11g, sig 25dBm, noise 28dBm>
21:25:12.755803 802.11: beacon, ssid (linksys), rates, ds, tim, erp, 47:1, xrates, vendor, <radiotap v0, chan 11, 11g, sig 23dBm, noise 28dBm>

I see in tcpdump output that all beacons are reported with chan 11,
where I know some of the access points are not on channel 11. Not
sure is this expected. Other than that, I don't see anything
concerning.

Regards,
 Mikolaj

> diff refs/heads/master refs/heads/iwm-multirx
> blob - 20ce2cbe3d2c2994b9a498965a3c9f29a5f31c9c
> blob + d3baf8473bd86beb05eaed7eb6cb715f90790c46
> --- sys/dev/pci/if_iwm.c
> +++ sys/dev/pci/if_iwm.c
> @@ -367,8 +367,10 @@ int iwm_get_signal_strength(struct iwm_softc *, struct
>  void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *,
>      struct iwm_rx_data *);
>  int iwm_get_noise(const struct iwm_statistics_rx_non_phy *);
> -void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *,
> -    struct iwm_rx_data *, struct mbuf_list *);
> +int iwm_rx_frame(struct iwm_softc *, struct mbuf *, uint32_t,
> +    struct mbuf_list *);
> +int iwm_ccmp_decap(struct iwm_softc *, struct mbuf *,
> +    struct ieee80211_node *);
>  void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *);
>  void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *,
>      struct iwm_node *);
> @@ -429,7 +431,7 @@ uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int)
>  int iwm_rval2ridx(int);
>  void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
>  void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *,
> -    struct iwm_mac_ctx_cmd *, uint32_t, int);
> +    struct iwm_mac_ctx_cmd *, uint32_t);
>  void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
>      struct iwm_mac_data_sta *, int);
>  int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
> @@ -470,6 +472,11 @@ const char *iwm_desc_lookup(uint32_t);
>  void iwm_nic_error(struct iwm_softc *);
>  void iwm_nic_umac_error(struct iwm_softc *);
>  #endif
> +void iwm_rx_mpdu(struct iwm_softc *, struct mbuf *, size_t,
> +    struct mbuf_list *);
> +int iwm_rx_pkt_valid(struct iwm_rx_packet *);
> +void iwm_rx_pkt(struct iwm_softc *, struct iwm_rx_data *,
> +    struct mbuf_list *);
>  void iwm_notif_intr(struct iwm_softc *);
>  int iwm_intr(void *);
>  int iwm_match(struct device *, void *, void *);
> @@ -1723,7 +1730,6 @@ iwm_nic_rx_init(struct iwm_softc *sc)
>      IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
>      IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |  /* HW bug */
>      IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
> -    IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
>      (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) |
>      IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
>      IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS);
> @@ -3429,56 +3435,24 @@ iwm_get_noise(const struct iwm_statistics_rx_non_phy *
>   return (nbant == 0) ? -127 : (total / nbant) - 107;
>  }
>  
> -void
> -iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
> -    struct iwm_rx_data *data, struct mbuf_list *ml)
> +int
> +iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, uint32_t rx_pkt_status,
> +    struct mbuf_list *ml)
>  {
>   struct ieee80211com *ic = &sc->sc_ic;
>   struct ieee80211_frame *wh;
>   struct ieee80211_node *ni;
>   struct ieee80211_rxinfo rxi;
>   struct ieee80211_channel *bss_chan;
> - struct mbuf *m;
>   struct iwm_rx_phy_info *phy_info;
> - struct iwm_rx_mpdu_res_start *rx_res;
>   int device_timestamp;
> - uint32_t len;
> - uint32_t rx_pkt_status;
>   int rssi, chanidx;
>   uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
>  
> - bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
> -    BUS_DMASYNC_POSTREAD);
> -
>   phy_info = &sc->sc_last_phy_info;
> - rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
> - wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res));
> - len = le16toh(rx_res->byte_count);
> - if (len < IEEE80211_MIN_LEN) {
> - ic->ic_stats.is_rx_tooshort++;
> - IC2IFP(ic)->if_ierrors++;
> - return;
> - }
> - if (len > IWM_RBUF_SIZE - sizeof(*rx_res)) {
> - IC2IFP(ic)->if_ierrors++;
> - return;
> - }
> - rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
> -    sizeof(*rx_res) + len));
> -
>   if (__predict_false(phy_info->cfg_phy_cnt > 20))
> - return;
> + return EINVAL;
>  
> - if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) ||
> -    !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK))
> - return; /* drop */
> -
> - m = data->m;
> - if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
> - return;
> - m->m_data = pkt->data + sizeof(*rx_res);
> - m->m_pkthdr.len = m->m_len = len;
> -
>   device_timestamp = le32toh(phy_info->system_timestamp);
>  
>   if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) {
> @@ -3493,6 +3467,7 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
>   if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))
>   chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
>  
> + wh = mtod(m, struct ieee80211_frame *);
>   ni = ieee80211_find_rxnode(ic, wh);
>   if (ni == ic->ic_bss) {
>   /*
> @@ -3572,6 +3547,8 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_pac
>   if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
>   ni->ni_chan = bss_chan;
>   ieee80211_release_node(ic, ni);
> +
> + return 0;
>  }
>  
>  void
> @@ -4521,6 +4498,7 @@ void
>  iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in,
>      struct iwm_mac_power_cmd *cmd)
>  {
> + struct ieee80211com *ic = &sc->sc_ic;
>   struct ieee80211_node *ni = &in->in_ni;
>   int dtim_period, dtim_msec, keep_alive;
>  
> @@ -4542,7 +4520,8 @@ iwm_power_build_cmd(struct iwm_softc *sc, struct iwm_n
>   keep_alive = roundup(keep_alive, 1000) / 1000;
>   cmd->keep_alive_seconds = htole16(keep_alive);
>  
> - cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
> + if (ic->ic_opmode != IEEE80211_M_MONITOR)
> + cmd->flags = htole16(IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK);
>  }
>  
>  int
> @@ -4569,13 +4548,15 @@ iwm_power_mac_update_mode(struct iwm_softc *sc, struct
>  int
>  iwm_power_update_device(struct iwm_softc *sc)
>  {
> - struct iwm_device_power_cmd cmd = {
> - .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
> - };
> + struct iwm_device_power_cmd cmd = { };
> + struct ieee80211com *ic = &sc->sc_ic;
>  
>   if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
>   return 0;
>  
> + if (ic->ic_opmode != IEEE80211_M_MONITOR)
> + cmd.flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
> +
>   return iwm_send_cmd_pdu(sc,
>      IWM_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
>  }
> @@ -4637,7 +4618,12 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node
>   add_sta_cmd.tfd_queue_msk |=
>      htole32(1 << iwm_ac_to_tx_fifo[ac]);
>   }
> - IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> +    etherbroadcastaddr);
> + else
> + IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
> +    in->in_ni.ni_bssid);
>   }
>   add_sta_cmd.add_modify = update ? 1 : 0;
>   add_sta_cmd.station_flags_msk
> @@ -5307,7 +5293,7 @@ iwm_ack_rates(struct iwm_softc *sc, struct iwm_node *i
>  
>  void
>  iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
> -    struct iwm_mac_ctx_cmd *cmd, uint32_t action, int assoc)
> +    struct iwm_mac_ctx_cmd *cmd, uint32_t action)
>  {
>  #define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
>   struct ieee80211com *ic = &sc->sc_ic;
> @@ -5319,12 +5305,21 @@ iwm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct i
>      in->in_color));
>   cmd->action = htole32(action);
>  
> - cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_LISTENER);
> + else if (ic->ic_opmode == IEEE80211_M_STA)
> + cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
> + else
> + panic("unsupported operating mode %d\n", ic->ic_opmode);
>   cmd->tsf_id = htole32(IWM_TSF_ID_A);
>  
>   IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_myaddr);
> - IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
> + return;
> + }
>  
> + IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
>   iwm_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
>   cmd->cck_rates = htole32(cck_ack_rates);
>   cmd->ofdm_rates = htole32(ofdm_ack_rates);
> @@ -5415,6 +5410,7 @@ int
>  iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action,
>      int assoc)
>  {
> + struct ieee80211com *ic = &sc->sc_ic;
>   struct ieee80211_node *ni = &in->in_ni;
>   struct iwm_mac_ctx_cmd cmd;
>   int active = (sc->sc_flags & IWM_FLAG_MAC_ACTIVE);
> @@ -5426,11 +5422,19 @@ iwm_mac_ctxt_cmd(struct iwm_softc *sc, struct iwm_node
>  
>   memset(&cmd, 0, sizeof(cmd));
>  
> - iwm_mac_ctxt_cmd_common(sc, in, &cmd, action, assoc);
> + iwm_mac_ctxt_cmd_common(sc, in, &cmd, action);
>  
> - /* Allow beacons to pass through as long as we are not associated or we
> - * do not have dtim period information */
> - if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_PROMISC |
> +    IWM_MAC_FILTER_IN_CONTROL_AND_MGMT |
> +    IWM_MAC_FILTER_IN_BEACON |
> +    IWM_MAC_FILTER_IN_PROBE_REQUEST |
> +    IWM_MAC_FILTER_IN_CRC32);
> + } else if (!assoc || !ni->ni_associd || !ni->ni_dtimperiod)
> + /*
> + * Allow beacons to pass through as long as we are not
> + * associated or we do not have dtim period information.
> + */
>   cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
>   else
>   iwm_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
> @@ -5654,7 +5658,10 @@ iwm_auth(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> - sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
> + else
> + sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
>   err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
>      IWM_FW_CTXT_ACTION_MODIFY, 0);
>   if (err) {
> @@ -5688,6 +5695,9 @@ iwm_auth(struct iwm_softc *sc)
>   }
>   sc->sc_flags |= IWM_FLAG_STA_ACTIVE;
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + return 0;
> +
>   /*
>   * Prevent the FW from wandering off channel during association
>   * by "protecting" the session with a time event.
> @@ -5818,8 +5828,16 @@ iwm_run(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + /* Add a MAC context and a sniffing STA. */
> + err = iwm_auth(sc);
> + if (err)
> + return err;
> + }
> +
>   /* Configure Rx chains for MIMO. */
> - if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
> + if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
> +    (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
>      !sc->sc_nvm.sku_cap_mimo_disable) {
>   err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
>      2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0);
> @@ -5887,6 +5905,11 @@ iwm_run(struct iwm_softc *sc)
>   ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn);
>   ieee80211_mira_node_init(&in->in_mn);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + iwm_led_blink_start(sc);
> + return 0;
> + }
> +
>   /* Start at lowest available bit-rate, AMRR will raise. */
>   in->in_ni.ni_txrate = 0;
>   in->in_ni.ni_txmcs = 0;
> @@ -5906,6 +5929,9 @@ iwm_run_stop(struct iwm_softc *sc)
>  
>   splassert(IPL_NET);
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR)
> + iwm_led_blink_stop(sc);
> +
>   err = iwm_sf_config(sc, IWM_SF_INIT_OFF);
>   if (err)
>   return err;
> @@ -6524,6 +6550,12 @@ iwm_init(struct ifnet *ifp)
>   ifq_clr_oactive(&ifp->if_snd);
>   ifp->if_flags |= IFF_RUNNING;
>  
> + if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> + ic->ic_bss->ni_chan = ic->ic_ibss_chan;
> + ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
> + return 0;
> + }
> +
>   ieee80211_begin_scan(ifp);
>  
>   /*
> @@ -7003,38 +7035,105 @@ do { \
>  #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT);
>  
>  void
> -iwm_notif_intr(struct iwm_softc *sc)
> +iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, size_t maxlen,
> +    struct mbuf_list *ml)
>  {
> - struct mbuf_list ml = MBUF_LIST_INITIALIZER();
> - uint16_t hw;
> + struct ieee80211com *ic = &sc->sc_ic;
> + struct ifnet *ifp = IC2IFP(ic);
> + struct iwm_rx_packet *pkt;
> + struct iwm_rx_mpdu_res_start *rx_res;
> + uint16_t len;
> + uint32_t rx_pkt_status;
> + int rxfail;
>  
> - bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
> -    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
> + pkt = mtod(m, struct iwm_rx_packet *);
> + rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data;
> + len = le16toh(rx_res->byte_count);
> + if (len < IEEE80211_MIN_LEN) {
> + ic->ic_stats.is_rx_tooshort++;
> + IC2IFP(ic)->if_ierrors++;
> + m_freem(m);
> + return;
> + }
> + if (len + sizeof(*rx_res) + sizeof(rx_pkt_status) > maxlen ||
> +    len > IEEE80211_MAX_LEN) {
> + IC2IFP(ic)->if_ierrors++;
> + m_freem(m);
> + return;
> + }
>  
> - hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
> - hw &= (IWM_RX_RING_COUNT - 1);
> - while (sc->rxq.cur != hw) {
> - struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
> - struct iwm_rx_packet *pkt;
> - int qid, idx, code, handled = 1;
> + memcpy(&rx_pkt_status, pkt->data + sizeof(*rx_res) + len,
> +    sizeof(rx_pkt_status));
> + rx_pkt_status = le32toh(rx_pkt_status);
> + rxfail = ((rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) == 0 ||
> +    (rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK) == 0);
> + if (rxfail) {
> + ifp->if_ierrors++;
> + m_freem(m);
> + return;
> + }
>  
> - bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt),
> -    BUS_DMASYNC_POSTREAD);
> - pkt = mtod(data->m, struct iwm_rx_packet *);
> + /* Extract the 802.11 frame. */
> + m->m_data = (caddr_t)pkt->data + sizeof(*rx_res);
> + m->m_pkthdr.len = m->m_len = len;
> + if (iwm_rx_frame(sc, m, rx_pkt_status, ml) != 0) {
> + ifp->if_ierrors++;
> + m_freem(m);
> + }
> +}
>  
> +int
> +iwm_rx_pkt_valid(struct iwm_rx_packet *pkt)
> +{
> + int qid, idx, code;
> +
> + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
> + qid = pkt->hdr.qid & ~0x80;
> + idx = pkt->hdr.idx;
> +
> + if ((code == 0 && qid == 0 && idx == 0) ||
> +    pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID))
> + return 0;
> +
> + return 1;
> +}
> +
> +void
> +iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data, struct mbuf_list *ml)
> +{
> + struct ifnet *ifp = IC2IFP(&sc->sc_ic);
> + struct iwm_rx_packet *pkt, *nextpkt;
> + uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
> + struct mbuf *m0, *m;
> + const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
> + size_t remain = IWM_RBUF_SIZE;
> + int qid, idx, code, handled = 1;
> +
> + bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE,
> +    BUS_DMASYNC_POSTREAD);
> +
> + m0 = data->m;
> + while (m0 && offset + minsz < IWM_RBUF_SIZE) {
> + pkt = (struct iwm_rx_packet *)(m0->m_data + offset);
> +
> + if (!iwm_rx_pkt_valid(pkt))
> + break;
> +
> + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
>   qid = pkt->hdr.qid & ~0x80;
>   idx = pkt->hdr.idx;
> + len = sizeof(pkt->len_n_flags) + iwm_rx_packet_len(pkt);
> + if (len < sizeof(pkt->hdr) ||
> +    len > (IWM_RBUF_SIZE - offset - minsz))
> + break;
>  
> - code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
> -
> - /*
> - * randomly get these from the firmware, no idea why.
> - * they at least seem harmless, so just ignore them for now
> - */
> - if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0)
> -    || pkt->len_n_flags == htole32(0x55550000))) {
> - ADVANCE_RXQ(sc);
> - continue;
> + if (code == IWM_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
> + /* Take mbuf m0 off the RX ring. */
> + if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur)) {
> + ifp->if_ierrors++;
> + break;
> + }
> + KASSERT(data->m != m0);
>   }
>  
>   switch (code) {
> @@ -7042,10 +7141,40 @@ iwm_notif_intr(struct iwm_softc *sc)
>   iwm_rx_rx_phy_cmd(sc, pkt, data);
>   break;
>  
> - case IWM_REPLY_RX_MPDU_CMD:
> - iwm_rx_rx_mpdu(sc, pkt, data, &ml);
> - break;
> + case IWM_REPLY_RX_MPDU_CMD: {
> + nextoff = offset +
> +    roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
> + nextpkt = (struct iwm_rx_packet *)
> +    (m0->m_data + nextoff);
> + if (nextoff + minsz >= IWM_RBUF_SIZE ||
> +    !iwm_rx_pkt_valid(nextpkt)) {
> + /* No need to copy last frame in buffer. */
> + if (offset > 0)
> + m_adj(m0, offset);
> + iwm_rx_mpdu(sc, m0, remain - minsz, ml);
> + m0 = NULL; /* stack owns m0 now; abort loop */
> + } else {
> + /*
> + * Create an mbuf which points to the current
> + * packet. Always copy from offset zero to
> + * preserve m_pkthdr.
> + */
> + m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
> + if (m == NULL) {
> + ifp->if_ierrors++;
> + break;
> + }
> + m_adj(m, offset);
> + iwm_rx_mpdu(sc, m, remain - minsz, ml);
> + }
>  
> + if (offset + minsz < remain)
> + remain -= offset;
> + else
> + remain = minsz;
> + break;
> + }
> +
>   case IWM_TX_CMD:
>   iwm_rx_tx_cmd(sc, pkt, data);
>   break;
> @@ -7288,6 +7417,27 @@ iwm_notif_intr(struct iwm_softc *sc)
>   iwm_cmd_done(sc, pkt);
>   }
>  
> + offset += roundup(len, IWM_FH_RSCSR_FRAME_ALIGN);
> + }
> +
> + if (m0 && m0 != data->m)
> + m_freem(m0);
> +}
> +
> +void
> +iwm_notif_intr(struct iwm_softc *sc)
> +{
> + struct mbuf_list ml = MBUF_LIST_INITIALIZER();
> + uint16_t hw;
> +
> + bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map,
> +    0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD);
> +
> + hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
> + hw &= (IWM_RX_RING_COUNT - 1);
> + while (sc->rxq.cur != hw) {
> + struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur];
> + iwm_rx_pkt(sc, data, &ml);
>   ADVANCE_RXQ(sc);
>   }
>   if_input(&sc->sc_ic.ic_if, &ml);
> @@ -7772,6 +7922,7 @@ iwm_attach(struct device *parent, struct device *self,
>      IEEE80211_C_RSN | /* WPA/RSN */
>      IEEE80211_C_SCANALL | /* device scans all channels at once */
>      IEEE80211_C_SCANALLBAND | /* device scans all bands at once */
> +    IEEE80211_C_MONITOR | /* monitor mode supported */
>      IEEE80211_C_SHSLOT | /* short slot time supported */
>      IEEE80211_C_SHPREAMBLE; /* short preamble supported */
>  
>

Reply | Threaded
Open this post in threaded view
|

Re: once again: iwm(4) multi-frame rx + monitor mode

Stefan Sperling-5
On Fri, Sep 13, 2019 at 09:35:56PM +0000, Mikolaj Kucharski wrote:

> I've tested monitor mode and it seems to work:
>
> $ ifconfig iwm0
> iwm0: flags=8847<UP,BROADCAST,DEBUG,RUNNING,SIMPLEX,MULTICAST> mtu 1500
>         lladdr 38:37:8b:XX:XX:XX
>         index 1 priority 4 llprio 3
>         groups: wlan egress
>         media: IEEE802.11 autoselect monitor
>         status: active
>         ieee80211: nwid linksys chan 11 bssid 00:1d:7e:XX:XX:XX 29%
>
> I've used kismet-201607R1p0 package and I could see networks and packets
> being reported. Testing with tcpdump(8) also showed traffic visible from
> various access points around:

Thanks for testing! I've never tried kismet myself, glad to see it works.
 

> # tcpdump -c 3 -y IEEE802_11_RADIO -ni iwm0  
> tcpdump: listening on iwm0, link-type IEEE802_11_RADIO
> 21:25:12.586606 802.11: beacon, ssid (net_092382), rates, ds, tim, xrates, rsn, htcaps, <radiotap v0, chan 11, 11g, sig 50dBm, noise 28dBm>
> 21:25:12.653411 802.11: beacon, ssid (linksys), rates, ds, tim, erp, 47:1, xrates, vendor, <radiotap v0, chan 11, 11g, sig 25dBm, noise 28dBm>
> 21:25:12.755803 802.11: beacon, ssid (linksys), rates, ds, tim, erp, 47:1, xrates, vendor, <radiotap v0, chan 11, 11g, sig 23dBm, noise 28dBm>
>
> I see in tcpdump output that all beacons are reported with chan 11,
> where I know some of the access points are not on channel 11. Not
> sure is this expected. Other than that, I don't see anything
> concerning.

This is likely cross-talk (several channels do overlap), or dual-beacons
deliberately sent by APs in 40 MHz mode on all channels they occupy.