Commit 5eae443e authored by Sara Sharon's avatar Sara Sharon Committed by Emmanuel Grumbach

iwlwifi: pcie: detect and workaround invalid write ptr behavior

In 9000 series A0 step the closed_rb_num is not wrapping around
properly. The queue is wrapping around as it should, so we can
W/A it by wrapping the closed_rb_num in the driver.
While at it, extend RX logging and add error handling of other
cases HW values may cause us to access invalid memory locations.
Add also a proper masking of vid value read from HW - this should
not have actual affect, but better to be on the safe side.
Signed-off-by: default avatarSara Sharon <sara.sharon@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent fcb6b92a
...@@ -1159,9 +1159,12 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) ...@@ -1159,9 +1159,12 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue)
r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
i = rxq->read; i = rxq->read;
/* W/A 9000 device step A0 wrap-around bug */
r &= (rxq->queue_size - 1);
/* Rx interrupt, but nothing sent from uCode */ /* Rx interrupt, but nothing sent from uCode */
if (i == r) if (i == r)
IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); IWL_DEBUG_RX(trans, "Q %d: HW = SW = %d\n", rxq->id, r);
while (i != r) { while (i != r) {
struct iwl_rx_mem_buffer *rxb; struct iwl_rx_mem_buffer *rxb;
...@@ -1174,15 +1177,18 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) ...@@ -1174,15 +1177,18 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue)
* used_bd is a 32 bit but only 12 are used to retrieve * used_bd is a 32 bit but only 12 are used to retrieve
* the vid * the vid
*/ */
u16 vid = (u16)le32_to_cpu(rxq->used_bd[i]); u16 vid = le32_to_cpu(rxq->used_bd[i]) & 0x0FFF;
if (WARN(vid >= ARRAY_SIZE(trans_pcie->global_table),
"Invalid rxb index from HW %u\n", (u32)vid))
goto out;
rxb = trans_pcie->global_table[vid]; rxb = trans_pcie->global_table[vid];
} else { } else {
rxb = rxq->queue[i]; rxb = rxq->queue[i];
rxq->queue[i] = NULL; rxq->queue[i] = NULL;
} }
IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i); IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency); iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency);
i = (i + 1) & (rxq->queue_size - 1); i = (i + 1) & (rxq->queue_size - 1);
...@@ -1245,7 +1251,7 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) ...@@ -1245,7 +1251,7 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue)
goto restart; goto restart;
} }
} }
out:
/* Backtrack one entry */ /* Backtrack one entry */
rxq->read = i; rxq->read = i;
spin_unlock(&rxq->lock); spin_unlock(&rxq->lock);
...@@ -1301,6 +1307,9 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) ...@@ -1301,6 +1307,9 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id)
struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry); struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry);
struct iwl_trans *trans = trans_pcie->trans; struct iwl_trans *trans = trans_pcie->trans;
if (WARN_ON(entry->entry >= trans->num_rx_queues))
return IRQ_NONE;
lock_map_acquire(&trans->sync_cmd_lockdep_map); lock_map_acquire(&trans->sync_cmd_lockdep_map);
local_bh_disable(); local_bh_disable();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment