@@ -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,