iwlwifi: pcie: skip fragmented receive buffers
authorJohannes Berg <johannes.berg@intel.com>
Sat, 25 Apr 2020 10:04:58 +0000 (13:04 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 8 May 2020 06:53:09 +0000 (09:53 +0300)
We don't really expect fragmented RBs, and don't seem to be seeing
them in practice since that would've caused a crash. Nevertheless,
we should be expecting the hardware to send them.

Parse the flag indicating a fragmented buffer, but then discard it
and any fragments thereof, at least for now. We need to do more
work in the higher layers to properly deal with this, since we may
not get "normal" firmware notifications that are fragmented, only
RX, and then we need to put it back together and add the necessary
API to report a chain of things to the higher layers, this doesn't
fit into the struct iwl_rx_cmd_buffer today.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20200425130140.e78a59f70b1d.Ica656a98a4e4220d73edc97600edd680cbc97241@changeid
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c

index 43f8120..b76c039 100644 (file)
@@ -189,6 +189,8 @@ struct iwl_rx_completion_desc {
  * @rb_stts_dma: bus address of receive buffer status
  * @lock:
  * @queue: actual rx queue. Not used for multi-rx queue.
+ * @next_rb_is_fragment: indicates that the previous RB that we handled set
+ *     the fragmented flag, so the next one is still another fragment
  *
  * NOTE:  rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers
  */
@@ -214,7 +216,7 @@ struct iwl_rxq {
        u32 queue_size;
        struct list_head rx_free;
        struct list_head rx_used;
-       bool need_update;
+       bool need_update, next_rb_is_fragment;
        void *rb_stts;
        dma_addr_t rb_stts_dma;
        spinlock_t lock;
index 8c29071..72d1cf2 100644 (file)
@@ -1427,7 +1427,8 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
 }
 
 static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans,
-                                                 struct iwl_rxq *rxq, int i)
+                                                 struct iwl_rxq *rxq, int i,
+                                                 bool *join)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rx_mem_buffer *rxb;
@@ -1441,10 +1442,12 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans,
                return rxb;
        }
 
-       if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
+       if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
                vid = le16_to_cpu(rxq->cd[i].rbid);
-       else
+               *join = rxq->cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED;
+       } else {
                vid = le32_to_cpu(rxq->bd_32[i]) & 0x0FFF; /* 12-bit VID */
+       }
 
        if (!vid || vid > RX_POOL_SIZE(trans_pcie->num_rx_bufs))
                goto out_err;
@@ -1502,6 +1505,7 @@ restart:
                u32 rb_pending_alloc =
                        atomic_read(&trans_pcie->rba.req_pending) *
                        RX_CLAIM_REQ_ALLOC;
+               bool join = false;
 
                if (unlikely(rb_pending_alloc >= rxq->queue_size / 2 &&
                             !emergency)) {
@@ -1514,11 +1518,29 @@ restart:
 
                IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
 
-               rxb = iwl_pcie_get_rxb(trans, rxq, i);
+               rxb = iwl_pcie_get_rxb(trans, rxq, i, &join);
                if (!rxb)
                        goto out;
 
-               iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency, i);
+               if (unlikely(join || rxq->next_rb_is_fragment)) {
+                       rxq->next_rb_is_fragment = join;
+                       /*
+                        * We can only get a multi-RB in the following cases:
+                        *  - firmware issue, sending a too big notification
+                        *  - sniffer mode with a large A-MSDU
+                        *  - large MTU frames (>2k)
+                        * since the multi-RB functionality is limited to newer
+                        * hardware that cannot put multiple entries into a
+                        * single RB.
+                        *
+                        * Right now, the higher layers aren't set up to deal
+                        * with that, so discard all of these.
+                        */
+                       list_add_tail(&rxb->list, &rxq->rx_free);
+                       rxq->free_count++;
+               } else {
+                       iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency, i);
+               }
 
                i = (i + 1) & (rxq->queue_size - 1);