Commit 2b3eb122 authored by Naftali Goldstein's avatar Naftali Goldstein Committed by Luca Coelho

iwlwifi: mvm: process ba-notifications also when sta rcu is invalid

The the driver prevents new Tx from being sent during the remove-station
flow is by invalidating the fw_id_to_mac_id rcu of that station.

However, if there was any Tx still in-flight (tx-cmd was sent but the
ba-notif wasn't received yet) the ba-response to those frames is simply
ignored without actually reclaiming anything. This later causes the
driver to think that that some of the station's queues aren't empty when
in practice they are which causes errors in the station remove flow.

Fix this by performing the tx-reclaim also if the rcu is invalid. any DB
that can't be updated due to this is not very important at this stage
since the station is about to be removed soon anyways.
Signed-off-by: default avatarNaftali Goldstein <naftali.goldstein@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20200926002540.72c604b4eda9.I21e75b31a9401870d18747355d4f4305b2fe1db8@changeidSigned-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 870bde78
...@@ -1768,9 +1768,9 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, ...@@ -1768,9 +1768,9 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
struct ieee80211_tx_info *ba_info, u32 rate) struct ieee80211_tx_info *ba_info, u32 rate)
{ {
struct sk_buff_head reclaimed_skbs; struct sk_buff_head reclaimed_skbs;
struct iwl_mvm_tid_data *tid_data; struct iwl_mvm_tid_data *tid_data = NULL;
struct ieee80211_sta *sta; struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvmsta; struct iwl_mvm_sta *mvmsta = NULL;
struct sk_buff *skb; struct sk_buff *skb;
int freed; int freed;
...@@ -1784,11 +1784,44 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, ...@@ -1784,11 +1784,44 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
/* Reclaiming frames for a station that has been deleted ? */ /* Reclaiming frames for a station that has been deleted ? */
if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
__skb_queue_head_init(&reclaimed_skbs);
/*
* Release all TFDs before the SSN, i.e. all TFDs in front of
* block-ack window (we assume that they've been successfully
* transmitted ... if not, it's too late anyway).
*/
iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs);
skb_queue_walk(&reclaimed_skbs, skb) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
memset(&info->status, 0, sizeof(info->status));
/* Packet was transmitted successfully, failures come as single
* frames because before failing a frame the firmware transmits
* it without aggregation at least once.
*/
info->flags |= IEEE80211_TX_STAT_ACK;
}
/*
* It's possible to get a BA response after invalidating the rcu (rcu is
* invalidated in order to prevent new Tx from being sent, but there may
* be some frames already in-flight).
* In this case we just want to reclaim, and could skip all the
* sta-dependent stuff since it's in the middle of being removed
* anyways.
*/
if (IS_ERR(sta))
goto out;
mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta = iwl_mvm_sta_from_mac80211(sta);
tid_data = &mvmsta->tid_data[tid]; tid_data = &mvmsta->tid_data[tid];
...@@ -1800,15 +1833,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, ...@@ -1800,15 +1833,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
return; return;
} }
__skb_queue_head_init(&reclaimed_skbs);
/*
* Release all TFDs before the SSN, i.e. all TFDs in front of
* block-ack window (we assume that they've been successfully
* transmitted ... if not, it's too late anyway).
*/
iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs);
spin_lock_bh(&mvmsta->lock); spin_lock_bh(&mvmsta->lock);
tid_data->next_reclaimed = index; tid_data->next_reclaimed = index;
...@@ -1832,15 +1856,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, ...@@ -1832,15 +1856,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid,
else else
WARN_ON_ONCE(tid != IWL_MAX_TID_COUNT); WARN_ON_ONCE(tid != IWL_MAX_TID_COUNT);
iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
memset(&info->status, 0, sizeof(info->status));
/* Packet was transmitted successfully, failures come as single
* frames because before failing a frame the firmware transmits
* it without aggregation at least once.
*/
info->flags |= IEEE80211_TX_STAT_ACK;
/* this is the first skb we deliver in this batch */ /* this is the first skb we deliver in this batch */
/* put the rate scaling data there */ /* put the rate scaling data there */
if (freed == 1) { if (freed == 1) {
...@@ -1917,8 +1932,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ...@@ -1917,8 +1932,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
rcu_read_lock(); rcu_read_lock();
mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id);
if (!mvmsta) /*
goto out_unlock; * It's possible to get a BA response after invalidating the rcu
* (rcu is invalidated in order to prevent new Tx from being
* sent, but there may be some frames already in-flight).
* In this case we just want to reclaim, and could skip all the
* sta-dependent stuff since it's in the middle of being removed
* anyways.
*/
/* Free per TID */ /* Free per TID */
for (i = 0; i < le16_to_cpu(ba_res->tfd_cnt); i++) { for (i = 0; i < le16_to_cpu(ba_res->tfd_cnt); i++) {
...@@ -1929,7 +1950,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ...@@ -1929,7 +1950,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
if (tid == IWL_MGMT_TID) if (tid == IWL_MGMT_TID)
tid = IWL_MAX_TID_COUNT; tid = IWL_MAX_TID_COUNT;
if (mvmsta)
mvmsta->tid_data[i].lq_color = lq_color; mvmsta->tid_data[i].lq_color = lq_color;
iwl_mvm_tx_reclaim(mvm, sta_id, tid, iwl_mvm_tx_reclaim(mvm, sta_id, tid,
(int)(le16_to_cpu(ba_tfd->q_num)), (int)(le16_to_cpu(ba_tfd->q_num)),
le16_to_cpu(ba_tfd->tfd_index), le16_to_cpu(ba_tfd->tfd_index),
...@@ -1937,9 +1960,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ...@@ -1937,9 +1960,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
le32_to_cpu(ba_res->tx_rate)); le32_to_cpu(ba_res->tx_rate));
} }
if (mvmsta)
iwl_mvm_tx_airtime(mvm, mvmsta, iwl_mvm_tx_airtime(mvm, mvmsta,
le32_to_cpu(ba_res->wireless_time)); le32_to_cpu(ba_res->wireless_time));
out_unlock:
rcu_read_unlock(); rcu_read_unlock();
out: out:
IWL_DEBUG_TX_REPLY(mvm, IWL_DEBUG_TX_REPLY(mvm,
......
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