diff options
author | Ilan Peer <ilan.peer@intel.com> | 2012-05-09 17:11:55 +0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-06-06 15:03:54 +0400 |
commit | 622a9268231bd6b486ec058a28f773fa89926012 (patch) | |
tree | 57c0eda48dfa100c44611ba7a88998009b7adfb4 /drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |
parent | e19ebcab01cc130fa832764d453b263460ec3b91 (diff) | |
download | linux-622a9268231bd6b486ec058a28f773fa89926012.tar.xz |
iwlwifi: handle race condition in ROC flow
When a remain on channel request from mac80211 is followed by
a request to tx a mgmt frame offchannel, it is possible that
the remain on channel expires before the device reported the
tx status for the frame. This causes a race condition in
mac80211.
To fix this, delay the ROC notification to mac80211 until the
device reported the Tx status for all frames in the aux queue.
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 3366e2e2f00f..ad1d36fa7642 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -486,6 +486,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + iwl_scan_offchannel_skb(priv); + return 0; drop_unlock_sta: @@ -1136,6 +1139,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + bool is_offchannel_skb; tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; @@ -1149,6 +1153,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, __skb_queue_head_init(&skbs); + is_offchannel_skb = false; + if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); @@ -1225,10 +1231,19 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + is_offchannel_skb = + (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } WARN_ON(!is_agg && freed != 1); + + /* + * An offchannel frame can be send only on the AUX queue, where + * there is no aggregation (and reordering) so it only is single + * skb is expected to be processed. + */ + WARN_ON(is_offchannel_skb && freed != 1); } iwl_check_abort_status(priv, tx_resp->frame_count, status); @@ -1239,6 +1254,9 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, ieee80211_tx_status(priv->hw, skb); } + if (is_offchannel_skb) + iwl_scan_offchannel_skb_status(priv); + return 0; } |