summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-06-13 11:45:59 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-21 18:21:30 -0700
commitf4ab9ece03dfc4b15c63b5fd5506183554976903 (patch)
treeb00c9ab3f0805e23977311a1aeb2be8543c673c9
parent65a1fb23bc5b2f21b44f8b81c89ccda808b3b321 (diff)
downloadlinux-3.10-f4ab9ece03dfc4b15c63b5fd5506183554976903.tar.gz
linux-3.10-f4ab9ece03dfc4b15c63b5fd5506183554976903.tar.bz2
linux-3.10-f4ab9ece03dfc4b15c63b5fd5506183554976903.zip
iwlwifi: pcie: fix race in queue unmapping
commit b967613d7e7c7bad176f5627c55e2d8c5aa2480e upstream. When a queue is disabled, it frees all its entries. Later, the op_mode might still get notifications from the firmware that triggers to free entries in the tx queue. The transport should be prepared for these races and know to ignore reclaim calls on queues that have been disabled and whose entries have been freed. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c10
1 files changed, 10 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index c5e30294c5a..51fca425def 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -576,9 +576,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
spin_lock_bh(&txq->lock);
while (q->write_ptr != q->read_ptr) {
+ IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
+ txq_id, q->read_ptr);
iwl_pcie_txq_free_tfd(trans, txq);
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
}
+ txq->active = false;
spin_unlock_bh(&txq->lock);
}
@@ -927,6 +930,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
spin_lock_bh(&txq->lock);
+ if (!txq->active) {
+ IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n",
+ txq_id, ssn);
+ goto out;
+ }
+
if (txq->q.read_ptr == tfd_num)
goto out;
@@ -1103,6 +1112,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
(fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
(1 << SCD_QUEUE_STTS_REG_POS_WSL) |
SCD_QUEUE_STTS_REG_MSK);
+ trans_pcie->txq[txq_id].active = true;
IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
txq_id, fifo, ssn & 0xff);
}