Return-path: Received: from xc.sipsolutions.net ([83.246.72.84]:53881 "EHLO sipsolutions.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758456AbZKYQv1 (ORCPT ); Wed, 25 Nov 2009 11:51:27 -0500 Message-Id: <20091125164821.184669673@sipsolutions.net> Date: Wed, 25 Nov 2009 17:46:17 +0100 From: Johannes Berg To: John Linville Cc: linux-wireless@vger.kernel.org Subject: [PATCH v2 3/5] mac80211: correctly place aMPDU RX reorder code References: <20091125164614.427835023@sipsolutions.net> Mime-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org List-ID: As indicated by the comment, the aMPDU RX reorder code should logically be after ieee80211_rx_h_check(). The previous patch moved the code there, and this patch now hooks it up in that place by introducing a list of skbs that are then processed by the remaining handlers. The list may be empty if the function is buffering the skb to release it later. The only change needed to the RX data is that the crypto handler needs to clear the key that may be set from a previous loop iteration, and that not everything can be in the rx flags now. Signed-off-by: Johannes Berg --- v2: ieee80211_rx_reorder_ampdu must not ignore the return value of ieee80211_sta_manage_reorder_buf() net/mac80211/ieee80211_i.h | 1 net/mac80211/rx.c | 168 +++++++++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 73 deletions(-) --- wireless-testing.orig/net/mac80211/rx.c 2009-11-25 16:23:44.000000000 +0100 +++ wireless-testing/net/mac80211/rx.c 2009-11-25 17:43:56.000000000 +0100 @@ -27,10 +27,6 @@ #include "tkip.h" #include "wme.h" -static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_rate *rate); - /* * monitor mode reception * @@ -555,7 +551,8 @@ static inline u16 seq_sub(u16 sq1, u16 s static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, - int index) + int index, + struct sk_buff_head *frames) { struct ieee80211_supported_band *sband; struct ieee80211_rate *rate = NULL; @@ -571,9 +568,9 @@ static void ieee80211_release_reorder_fr sband = hw->wiphy->bands[status->band]; if (!(status->flag & RX_FLAG_HT)) rate = &sband->bitrates[status->rate_idx]; - __ieee80211_rx_handle_packet(hw, skb, rate); tid_agg_rx->stored_mpdu_num--; tid_agg_rx->reorder_buf[index] = NULL; + skb_queue_tail(frames, skb); no_frame: tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); @@ -581,14 +578,15 @@ no_frame: static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, - u16 head_seq_num) + u16 head_seq_num, + struct sk_buff_head *frames) { int index; while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; - ieee80211_release_reorder_frame(hw, tid_agg_rx, index); + ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames); } } @@ -608,7 +606,8 @@ static void ieee80211_release_reorder_fr */ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, - struct sk_buff *skb) + struct sk_buff *skb, + struct sk_buff_head *frames) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 sc = le16_to_cpu(hdr->seq_ctrl); @@ -632,7 +631,8 @@ static bool ieee80211_sta_manage_reorder if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) { head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); /* release stored frames up to new head to stack */ - ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num); + ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num, + frames); } /* Now the new frame is always in the range of the reordering buffer */ @@ -687,7 +687,8 @@ static bool ieee80211_sta_manage_reorder "frames\n", wiphy_name(hw->wiphy)); #endif - ieee80211_release_reorder_frame(hw, tid_agg_rx, j); + ieee80211_release_reorder_frame(hw, tid_agg_rx, + j, frames); /* * Increment the head seq# also for the skipped slots. @@ -697,7 +698,7 @@ static bool ieee80211_sta_manage_reorder skipped = 0; } } else while (tid_agg_rx->reorder_buf[index]) { - ieee80211_release_reorder_frame(hw, tid_agg_rx, index); + ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames); index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; } @@ -709,38 +710,39 @@ static bool ieee80211_sta_manage_reorder * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns * true if the MPDU was buffered, false if it should be processed. */ -static bool ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, - struct sk_buff *skb) +static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, + struct sk_buff_head *frames) { + struct sk_buff *skb = rx->skb; + struct ieee80211_local *local = rx->local; struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct sta_info *sta; + struct sta_info *sta = rx->sta; struct tid_ampdu_rx *tid_agg_rx; u16 sc; int tid; if (!ieee80211_is_data_qos(hdr->frame_control)) - return false; + goto dont_reorder; /* * filter the QoS data rx stream according to * STA/TID and check if this STA/TID is on aggregation */ - sta = sta_info_get(local, hdr->addr2); if (!sta) - return false; + goto dont_reorder; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) - return false; + goto dont_reorder; tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; /* qos null data frames are excluded */ if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) - return false; + goto dont_reorder; /* new, potentially un-ordered, ampdu frame - process it */ @@ -755,10 +757,14 @@ static bool ieee80211_rx_reorder_ampdu(s ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr, tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP); dev_kfree_skb(skb); - return true; + return; } - return ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb); + if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) + return; + + dont_reorder: + __skb_queue_tail(frames, skb); } static ieee80211_rx_result debug_noinline @@ -863,6 +869,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_ if (!(rx->flags & IEEE80211_RX_RA_MATCH)) return RX_CONTINUE; + /* start without a key */ + rx->key = NULL; + if (rx->sta) stakey = rcu_dereference(rx->sta->key); @@ -1815,7 +1824,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_ } static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) +ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) { struct ieee80211_local *local = rx->local; struct ieee80211_hw *hw = &local->hw; @@ -1845,7 +1854,8 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_ TU_TO_EXP_TIME(tid_agg_rx->timeout)); /* release stored frames up to start of BAR */ - ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num); + ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num, + frames); kfree_skb(skb); return RX_QUEUED; } @@ -2168,8 +2178,11 @@ static void ieee80211_invoke_rx_handlers struct sk_buff *skb, struct ieee80211_rate *rate) { + struct sk_buff_head reorder_release; ieee80211_rx_result res = RX_DROP_MONITOR; + __skb_queue_head_init(&reorder_release); + rx->skb = skb; rx->sdata = sdata; @@ -2177,50 +2190,72 @@ static void ieee80211_invoke_rx_handlers do { \ res = rxh(rx); \ if (res != RX_CONTINUE) \ - goto rxh_done; \ + goto rxh_next; \ } while (0); + /* + * NB: the rxh_next label works even if we jump + * to it from here because then the list will + * be empty, which is a trivial check + */ CALL_RXH(ieee80211_rx_h_passive_scan) CALL_RXH(ieee80211_rx_h_check) - CALL_RXH(ieee80211_rx_h_decrypt) - CALL_RXH(ieee80211_rx_h_check_more_data) - CALL_RXH(ieee80211_rx_h_sta_process) - CALL_RXH(ieee80211_rx_h_defragment) - CALL_RXH(ieee80211_rx_h_ps_poll) - CALL_RXH(ieee80211_rx_h_michael_mic_verify) - /* must be after MMIC verify so header is counted in MPDU mic */ - CALL_RXH(ieee80211_rx_h_remove_qos_control) - CALL_RXH(ieee80211_rx_h_amsdu) + + ieee80211_rx_reorder_ampdu(rx, &reorder_release); + + while ((skb = __skb_dequeue(&reorder_release))) { + /* + * all the other fields are valid across frames + * that belong to an aMPDU since they are on the + * same TID from the same station + */ + rx->skb = skb; + + CALL_RXH(ieee80211_rx_h_decrypt) + CALL_RXH(ieee80211_rx_h_check_more_data) + CALL_RXH(ieee80211_rx_h_sta_process) + CALL_RXH(ieee80211_rx_h_defragment) + CALL_RXH(ieee80211_rx_h_ps_poll) + CALL_RXH(ieee80211_rx_h_michael_mic_verify) + /* must be after MMIC verify so header is counted in MPDU mic */ + CALL_RXH(ieee80211_rx_h_remove_qos_control) + CALL_RXH(ieee80211_rx_h_amsdu) #ifdef CONFIG_MAC80211_MESH - if (ieee80211_vif_is_mesh(&sdata->vif)) - CALL_RXH(ieee80211_rx_h_mesh_fwding); + if (ieee80211_vif_is_mesh(&sdata->vif)) + CALL_RXH(ieee80211_rx_h_mesh_fwding); #endif - CALL_RXH(ieee80211_rx_h_data) - CALL_RXH(ieee80211_rx_h_ctrl) - CALL_RXH(ieee80211_rx_h_action) - CALL_RXH(ieee80211_rx_h_mgmt) + CALL_RXH(ieee80211_rx_h_data) + + /* special treatment -- needs the queue */ + res = ieee80211_rx_h_ctrl(rx, &reorder_release); + if (res != RX_CONTINUE) + goto rxh_next; + + CALL_RXH(ieee80211_rx_h_action) + CALL_RXH(ieee80211_rx_h_mgmt) #undef CALL_RXH - rxh_done: - switch (res) { - case RX_DROP_MONITOR: - I802_DEBUG_INC(sdata->local->rx_handlers_drop); - if (rx->sta) - rx->sta->rx_dropped++; - /* fall through */ - case RX_CONTINUE: - ieee80211_rx_cooked_monitor(rx, rate); - break; - case RX_DROP_UNUSABLE: - I802_DEBUG_INC(sdata->local->rx_handlers_drop); - if (rx->sta) - rx->sta->rx_dropped++; - dev_kfree_skb(rx->skb); - break; - case RX_QUEUED: - I802_DEBUG_INC(sdata->local->rx_handlers_queued); - break; + rxh_next: + switch (res) { + case RX_DROP_MONITOR: + I802_DEBUG_INC(sdata->local->rx_handlers_drop); + if (rx->sta) + rx->sta->rx_dropped++; + /* fall through */ + case RX_CONTINUE: + ieee80211_rx_cooked_monitor(rx, rate); + break; + case RX_DROP_UNUSABLE: + I802_DEBUG_INC(sdata->local->rx_handlers_drop); + if (rx->sta) + rx->sta->rx_dropped++; + dev_kfree_skb(rx->skb); + break; + case RX_QUEUED: + I802_DEBUG_INC(sdata->local->rx_handlers_queued); + break; + } } } @@ -2494,20 +2529,7 @@ void ieee80211_rx(struct ieee80211_hw *h return; } - /* - * In theory, the block ack reordering should happen after duplicate - * removal (ieee80211_rx_h_check(), which is an RX handler). As such, - * the call to ieee80211_rx_reorder_ampdu() should really be moved to - * happen as a new RX handler between ieee80211_rx_h_check and - * ieee80211_rx_h_decrypt. This cleanup may eventually happen, but for - * the time being, the call can be here since RX reorder buf processing - * will implicitly skip duplicates. We could, in theory at least, - * process frames that ieee80211_rx_h_passive_scan would drop (e.g., - * frames from other than operational channel), but that should not - * happen in normal networks. - */ - if (!ieee80211_rx_reorder_ampdu(local, skb)) - __ieee80211_rx_handle_packet(hw, skb, rate); + __ieee80211_rx_handle_packet(hw, skb, rate); rcu_read_unlock(); --- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-11-25 16:23:43.000000000 +0100 +++ wireless-testing/net/mac80211/ieee80211_i.h 2009-11-25 16:24:18.000000000 +0100 @@ -164,6 +164,7 @@ typedef unsigned __bitwise__ ieee80211_r #define IEEE80211_RX_RA_MATCH BIT(1) #define IEEE80211_RX_AMSDU BIT(2) #define IEEE80211_RX_FRAGMENTED BIT(3) +/* only add flags here that do not change with subframes of an aMPDU */ struct ieee80211_rx_data { struct sk_buff *skb;