summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-06-10 10:21:36 +0200
committerJohn W. Linville <linville@tuxdriver.com>2010-06-14 15:39:27 -0400
commit344eec67c7b8557234e149d254bca2ae9614d61e (patch)
treea7243e1f8c67adf535be457b4ccccb212259f41c
parentbed7ee6e44cb7633a4f9821688a6c7ae977615ed (diff)
mac80211: move blockack stop due to fragmentation
There's a corner case where we receive a fragmented frame during a blockack session, in which case we will terminate that session. To simplify future work in this area that will culminate in allowing the driver callbacks for aggregation to sleep, move the processing of this case out of the RX path into the interface work. This will simplify future work because the new place for this code doesn't require that the function will always be atomic, which the RX path needs. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/mac80211/iface.c32
-rw-r--r--net/mac80211/rx.c5
2 files changed, 33 insertions, 4 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7a3dbde9979..87fc012e4ab 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -707,6 +707,7 @@ static void ieee80211_iface_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data, work);
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
+ struct sta_info *sta;
if (!ieee80211_sdata_running(sdata))
return;
@@ -729,7 +730,6 @@ static void ieee80211_iface_work(struct work_struct *work)
if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len;
- struct sta_info *sta;
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
@@ -753,6 +753,36 @@ static void ieee80211_iface_work(struct work_struct *work)
}
}
rcu_read_unlock();
+ } else if (ieee80211_is_data_qos(mgmt->frame_control)) {
+ struct ieee80211_hdr *hdr = (void *)mgmt;
+ /*
+ * So the frame isn't mgmt, but frame_control
+ * is at the right place anyway, of course, so
+ * the if statement is correct.
+ *
+ * Warn if we have other data frame types here,
+ * they must not get here.
+ */
+ WARN_ON(hdr->frame_control &
+ cpu_to_le16(IEEE80211_STYPE_NULLFUNC));
+ WARN_ON(!(hdr->seq_ctrl &
+ cpu_to_le16(IEEE80211_SCTL_FRAG)));
+ /*
+ * This was a fragment of a frame, received while
+ * a block-ack session was active. That cannot be
+ * right, so terminate the session.
+ */
+ rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->sa);
+ if (sta) {
+ u16 tid = *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_TID_MASK;
+
+ __ieee80211_stop_rx_ba_session(
+ sta, tid, WLAN_BACK_RECIPIENT,
+ WLAN_REASON_QSTA_REQUIRE_SETUP);
+ }
+ rcu_read_unlock();
} else switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ieee80211_sta_rx_queued_mgmt(sdata, skb);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 435c2166e0c..b716fa2370b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -741,9 +741,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
spin_unlock(&sta->lock);
- __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
- dev_kfree_skb(skb);
+ skb_queue_tail(&rx->sdata->skb_queue, skb);
+ ieee80211_queue_work(&local->hw, &rx->sdata->work);
return;
}