From 2b3eb122342c72ee18cb26b0a28f7a66612f7992 Mon Sep 17 00:00:00 2001 From: Naftali Goldstein Date: Sat, 26 Sep 2020 00:30:46 +0300 Subject: [PATCH] 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: Naftali Goldstein Signed-off-by: Luca Coelho Link: https://lore.kernel.org/r/iwlwifi.20200926002540.72c604b4eda9.I21e75b31a9401870d18747355d4f4305b2fe1db8@changeid Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 77 +++++++++++++-------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 2f6484e0d726..a372f32f4571 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -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 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 iwl_mvm_sta *mvmsta; + struct iwl_mvm_sta *mvmsta = NULL; struct sk_buff *skb; int freed; @@ -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]); /* 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(); 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); 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, 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); tid_data->next_reclaimed = index; @@ -1832,15 +1856,6 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, else 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 */ /* put the rate scaling data there */ if (freed == 1) { @@ -1917,8 +1932,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) rcu_read_lock(); 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 */ 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) if (tid == IWL_MGMT_TID) tid = IWL_MAX_TID_COUNT; - mvmsta->tid_data[i].lq_color = lq_color; + if (mvmsta) + mvmsta->tid_data[i].lq_color = lq_color; + iwl_mvm_tx_reclaim(mvm, sta_id, tid, (int)(le16_to_cpu(ba_tfd->q_num)), 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) le32_to_cpu(ba_res->tx_rate)); } - iwl_mvm_tx_airtime(mvm, mvmsta, - le32_to_cpu(ba_res->wireless_time)); -out_unlock: + if (mvmsta) + iwl_mvm_tx_airtime(mvm, mvmsta, + le32_to_cpu(ba_res->wireless_time)); rcu_read_unlock(); out: IWL_DEBUG_TX_REPLY(mvm, -- GitLab