|
Jiri Slaby |
896490 |
From: Johannes Berg <johannes.berg@intel.com>
|
|
Jiri Slaby |
896490 |
Date: Fri, 17 Mar 2023 10:53:25 +0100
|
|
Jiri Slaby |
896490 |
Subject: [PATCH] wifi: iwlwifi: mvm: protect TXQ list manipulation
|
|
Jiri Slaby |
896490 |
References: bsc#1012628
|
|
Jiri Slaby |
896490 |
Patch-mainline: 6.2.12
|
|
Jiri Slaby |
896490 |
Git-commit: 923bf981eb6ecc027227716e30701bdcc1845fbf
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
[ Upstream commit 923bf981eb6ecc027227716e30701bdcc1845fbf ]
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
Some recent upstream debugging uncovered the fact that in
|
|
Jiri Slaby |
896490 |
iwlwifi, the TXQ list manipulation is racy.
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
Introduce a new state bit for when the TXQ is completely
|
|
Jiri Slaby |
896490 |
ready and can be used without locking, and if that's not
|
|
Jiri Slaby |
896490 |
set yet acquire the lock to check everything correctly.
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
Reviewed-by: Benjamin Berg <benjamin.berg@intel.com>
|
|
Jiri Slaby |
896490 |
Tested-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
|
|
Jiri Slaby |
896490 |
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
|
Jiri Slaby |
896490 |
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
|
Jiri Slaby |
896490 |
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
|
|
Jiri Slaby |
896490 |
---
|
|
Jiri Slaby |
896490 |
.../net/wireless/intel/iwlwifi/mvm/mac80211.c | 45 ++++++-------------
|
|
Jiri Slaby |
896490 |
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +
|
|
Jiri Slaby |
896490 |
drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 +
|
|
Jiri Slaby |
896490 |
drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 25 +++++++++--
|
|
Jiri Slaby |
896490 |
4 files changed, 39 insertions(+), 34 deletions(-)
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
|
|
Jiri Slaby |
896490 |
index 5b497418..1d46a2b3 100644
|
|
Jiri Slaby |
896490 |
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
|
|
Jiri Slaby |
896490 |
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
|
|
Jiri Slaby |
896490 |
@@ -760,42 +760,25 @@ static void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
|
|
Jiri Slaby |
896490 |
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
|
Jiri Slaby |
896490 |
struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- /*
|
|
Jiri Slaby |
896490 |
- * Please note that racing is handled very carefully here:
|
|
Jiri Slaby |
896490 |
- * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is
|
|
Jiri Slaby |
896490 |
- * deleted afterwards.
|
|
Jiri Slaby |
896490 |
- * This means that if:
|
|
Jiri Slaby |
896490 |
- * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list):
|
|
Jiri Slaby |
896490 |
- * queue is allocated and we can TX.
|
|
Jiri Slaby |
896490 |
- * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list):
|
|
Jiri Slaby |
896490 |
- * a race, should defer the frame.
|
|
Jiri Slaby |
896490 |
- * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list):
|
|
Jiri Slaby |
896490 |
- * need to allocate the queue and defer the frame.
|
|
Jiri Slaby |
896490 |
- * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list):
|
|
Jiri Slaby |
896490 |
- * queue is already scheduled for allocation, no need to allocate,
|
|
Jiri Slaby |
896490 |
- * should defer the frame.
|
|
Jiri Slaby |
896490 |
- */
|
|
Jiri Slaby |
896490 |
-
|
|
Jiri Slaby |
896490 |
- /* If the queue is allocated TX and return. */
|
|
Jiri Slaby |
896490 |
- if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) {
|
|
Jiri Slaby |
896490 |
- /*
|
|
Jiri Slaby |
896490 |
- * Check that list is empty to avoid a race where txq_id is
|
|
Jiri Slaby |
896490 |
- * already updated, but the queue allocation work wasn't
|
|
Jiri Slaby |
896490 |
- * finished
|
|
Jiri Slaby |
896490 |
- */
|
|
Jiri Slaby |
896490 |
- if (unlikely(txq->sta && !list_empty(&mvmtxq->list)))
|
|
Jiri Slaby |
896490 |
- return;
|
|
Jiri Slaby |
896490 |
-
|
|
Jiri Slaby |
896490 |
+ if (likely(test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) ||
|
|
Jiri Slaby |
896490 |
+ !txq->sta) {
|
|
Jiri Slaby |
896490 |
iwl_mvm_mac_itxq_xmit(hw, txq);
|
|
Jiri Slaby |
896490 |
return;
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- /* The list is being deleted only after the queue is fully allocated. */
|
|
Jiri Slaby |
896490 |
- if (!list_empty(&mvmtxq->list))
|
|
Jiri Slaby |
896490 |
- return;
|
|
Jiri Slaby |
896490 |
+ /* iwl_mvm_mac_itxq_xmit() will later be called by the worker
|
|
Jiri Slaby |
896490 |
+ * to handle any packets we leave on the txq now
|
|
Jiri Slaby |
896490 |
+ */
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
|
|
Jiri Slaby |
896490 |
- schedule_work(&mvm->add_stream_wk);
|
|
Jiri Slaby |
896490 |
+ spin_lock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
+ /* The list is being deleted only after the queue is fully allocated. */
|
|
Jiri Slaby |
896490 |
+ if (list_empty(&mvmtxq->list) &&
|
|
Jiri Slaby |
896490 |
+ /* recheck under lock */
|
|
Jiri Slaby |
896490 |
+ !test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) {
|
|
Jiri Slaby |
896490 |
+ list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
|
|
Jiri Slaby |
896490 |
+ schedule_work(&mvm->add_stream_wk);
|
|
Jiri Slaby |
896490 |
+ }
|
|
Jiri Slaby |
896490 |
+ spin_unlock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
|
|
Jiri Slaby |
896490 |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
|
|
Jiri Slaby |
896490 |
index 3146b3d0..157de77e 100644
|
|
Jiri Slaby |
896490 |
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
|
|
Jiri Slaby |
896490 |
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
|
|
Jiri Slaby |
896490 |
@@ -731,6 +731,7 @@ struct iwl_mvm_txq {
|
|
Jiri Slaby |
896490 |
atomic_t tx_request;
|
|
Jiri Slaby |
896490 |
#define IWL_MVM_TXQ_STATE_STOP_FULL 0
|
|
Jiri Slaby |
896490 |
#define IWL_MVM_TXQ_STATE_STOP_REDIRECT 1
|
|
Jiri Slaby |
896490 |
+#define IWL_MVM_TXQ_STATE_READY 2
|
|
Jiri Slaby |
896490 |
unsigned long state;
|
|
Jiri Slaby |
896490 |
};
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
@@ -829,6 +830,7 @@ struct iwl_mvm {
|
|
Jiri Slaby |
896490 |
struct iwl_mvm_tvqm_txq_info tvqm_info[IWL_MAX_TVQM_QUEUES];
|
|
Jiri Slaby |
896490 |
};
|
|
Jiri Slaby |
896490 |
struct work_struct add_stream_wk; /* To add streams to queues */
|
|
Jiri Slaby |
896490 |
+ spinlock_t add_stream_lock;
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
const char *nvm_file_name;
|
|
Jiri Slaby |
896490 |
struct iwl_nvm_data *nvm_data;
|
|
Jiri Slaby |
896490 |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
|
|
Jiri Slaby |
896490 |
index f43e617f..c49a2a1e 100644
|
|
Jiri Slaby |
896490 |
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
|
|
Jiri Slaby |
896490 |
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
|
|
Jiri Slaby |
896490 |
@@ -1194,6 +1194,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|
Jiri Slaby |
896490 |
INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk);
|
|
Jiri Slaby |
896490 |
INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk);
|
|
Jiri Slaby |
896490 |
INIT_LIST_HEAD(&mvm->add_stream_txqs);
|
|
Jiri Slaby |
896490 |
+ spin_lock_init(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
init_waitqueue_head(&mvm->rx_sync_waitq);
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
|
|
Jiri Slaby |
896490 |
index 21ad7b85..9caae779 100644
|
|
Jiri Slaby |
896490 |
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
|
|
Jiri Slaby |
896490 |
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
|
|
Jiri Slaby |
896490 |
@@ -384,8 +384,11 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
|
Jiri Slaby |
896490 |
struct iwl_mvm_txq *mvmtxq =
|
|
Jiri Slaby |
896490 |
iwl_mvm_txq_from_tid(sta, tid);
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
+ spin_lock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state);
|
|
Jiri Slaby |
896490 |
+ mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
+ spin_unlock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
/* Regardless if this is a reserved TXQ for a STA - mark it as false */
|
|
Jiri Slaby |
896490 |
@@ -479,8 +482,11 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
|
|
Jiri Slaby |
896490 |
disable_agg_tids |= BIT(tid);
|
|
Jiri Slaby |
896490 |
mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
+ spin_lock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state);
|
|
Jiri Slaby |
896490 |
+ mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
+ spin_unlock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */
|
|
Jiri Slaby |
896490 |
@@ -1444,12 +1450,22 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk)
|
|
Jiri Slaby |
896490 |
* a queue in the function itself.
|
|
Jiri Slaby |
896490 |
*/
|
|
Jiri Slaby |
896490 |
if (iwl_mvm_sta_alloc_queue(mvm, txq->sta, txq->ac, tid)) {
|
|
Jiri Slaby |
896490 |
+ spin_lock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ spin_unlock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
continue;
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
- list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ /* now we're ready, any remaining races/concurrency will be
|
|
Jiri Slaby |
896490 |
+ * handled in iwl_mvm_mac_itxq_xmit()
|
|
Jiri Slaby |
896490 |
+ */
|
|
Jiri Slaby |
896490 |
+ set_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state);
|
|
Jiri Slaby |
896490 |
+
|
|
Jiri Slaby |
896490 |
local_bh_disable();
|
|
Jiri Slaby |
896490 |
+ spin_lock(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
+ list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ spin_unlock(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
+
|
|
Jiri Slaby |
896490 |
iwl_mvm_mac_itxq_xmit(mvm->hw, txq);
|
|
Jiri Slaby |
896490 |
local_bh_enable();
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
@@ -1864,8 +1880,11 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm,
|
|
Jiri Slaby |
896490 |
struct iwl_mvm_txq *mvmtxq =
|
|
Jiri Slaby |
896490 |
iwl_mvm_txq_from_mac80211(sta->txq[i]);
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
+ spin_lock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
|
|
Jiri Slaby |
896490 |
list_del_init(&mvmtxq->list);
|
|
Jiri Slaby |
896490 |
+ clear_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state);
|
|
Jiri Slaby |
896490 |
+ spin_unlock_bh(&mvm->add_stream_lock);
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
}
|
|
Jiri Slaby |
896490 |
|
|
Jiri Slaby |
896490 |
--
|
|
Jiri Slaby |
896490 |
2.35.3
|
|
Jiri Slaby |
896490 |
|