Commit 06470f74 authored by Sara Sharon's avatar Sara Sharon Committed by Johannes Berg

mac80211: add API to allow filtering frames in BA sessions

If any frames are dropped that are part of a BA session, the reorder
buffer will "indefinitely" (until the timeout) wait for them to come
in (or a BAR moving the window) and won't release frames after them.
This means it isn't possible to filter frames within a BA session in
firmware.

Introduce an API function that allows such filtering. Calling this
function will move the BA window forward to the new SSN, and allows
marking frames after the SSN as having been filtered, so any future
reordering activity will release frames while skipping the holes.
Signed-off-by: default avatarSara Sharon <sara.sharon@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent fb4ea054
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 Intel Deutschland GmbH * Copyright (C) 2015 - 2016 Intel Deutschland GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -5193,6 +5193,24 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); ...@@ -5193,6 +5193,24 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw);
void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
const u8 *addr); const u8 *addr);
/**
* ieee80211_mark_rx_ba_filtered_frames - move RX BA window and mark filtered
* @pubsta: station struct
* @tid: the session's TID
* @ssn: starting sequence number of the bitmap, all frames before this are
* assumed to be out of the window after the call
* @filtered: bitmap of filtered frames, BIT(0) is the @ssn entry etc.
* @received_mpdus: number of received mpdus in firmware
*
* This function moves the BA window and releases all frames before @ssn, and
* marks frames marked in the bitmap as having been filtered. Afterwards, it
* checks if any frames in the window starting from @ssn can now be released
* (in case they were only waiting for frames that were filtered.)
*/
void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
u16 ssn, u64 filtered,
u16 received_mpdus);
/** /**
* ieee80211_send_bar - send a BlockAckReq frame * ieee80211_send_bar - send a BlockAckReq frame
* *
......
...@@ -376,6 +376,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, ...@@ -376,6 +376,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
tid_agg_rx->timeout = timeout; tid_agg_rx->timeout = timeout;
tid_agg_rx->stored_mpdu_num = 0; tid_agg_rx->stored_mpdu_num = 0;
tid_agg_rx->auto_seq = auto_seq; tid_agg_rx->auto_seq = auto_seq;
tid_agg_rx->reorder_buf_filtered = 0;
status = WLAN_STATUS_SUCCESS; status = WLAN_STATUS_SUCCESS;
/* activate it for RX */ /* activate it for RX */
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/bitops.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <net/ieee80211_radiotap.h> #include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -806,6 +807,9 @@ static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx, ...@@ -806,6 +807,9 @@ static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx,
struct sk_buff *tail = skb_peek_tail(frames); struct sk_buff *tail = skb_peek_tail(frames);
struct ieee80211_rx_status *status; struct ieee80211_rx_status *status;
if (tid_agg_rx->reorder_buf_filtered & BIT_ULL(index))
return true;
if (!tail) if (!tail)
return false; return false;
...@@ -844,6 +848,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, ...@@ -844,6 +848,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
} }
no_frame: no_frame:
tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
} }
...@@ -3300,6 +3305,85 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) ...@@ -3300,6 +3305,85 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
ieee80211_rx_handlers(&rx, &frames); ieee80211_rx_handlers(&rx, &frames);
} }
void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
u16 ssn, u64 filtered,
u16 received_mpdus)
{
struct sta_info *sta;
struct tid_ampdu_rx *tid_agg_rx;
struct sk_buff_head frames;
struct ieee80211_rx_data rx = {
/* This is OK -- must be QoS data frame */
.security_idx = tid,
.seqno_idx = tid,
};
int i, diff;
if (WARN_ON(!pubsta || tid >= IEEE80211_NUM_TIDS))
return;
__skb_queue_head_init(&frames);
sta = container_of(pubsta, struct sta_info, sta);
rx.sta = sta;
rx.sdata = sta->sdata;
rx.local = sta->local;
rcu_read_lock();
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx)
goto out;
spin_lock_bh(&tid_agg_rx->reorder_lock);
if (received_mpdus >= IEEE80211_SN_MODULO >> 1) {
int release;
/* release all frames in the reorder buffer */
release = (tid_agg_rx->head_seq_num + tid_agg_rx->buf_size) %
IEEE80211_SN_MODULO;
ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx,
release, &frames);
/* update ssn to match received ssn */
tid_agg_rx->head_seq_num = ssn;
} else {
ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, ssn,
&frames);
}
/* handle the case that received ssn is behind the mac ssn.
* it can be tid_agg_rx->buf_size behind and still be valid */
diff = (tid_agg_rx->head_seq_num - ssn) & IEEE80211_SN_MASK;
if (diff >= tid_agg_rx->buf_size) {
tid_agg_rx->reorder_buf_filtered = 0;
goto release;
}
filtered = filtered >> diff;
ssn += diff;
/* update bitmap */
for (i = 0; i < tid_agg_rx->buf_size; i++) {
int index = (ssn + i) % tid_agg_rx->buf_size;
tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
if (filtered & BIT_ULL(i))
tid_agg_rx->reorder_buf_filtered |= BIT_ULL(index);
}
/* now process also frames that the filter marking released */
ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames);
release:
spin_unlock_bh(&tid_agg_rx->reorder_lock);
ieee80211_rx_handlers(&rx, &frames);
out:
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_mark_rx_ba_filtered_frames);
/* main receive path */ /* main receive path */
static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
......
...@@ -168,6 +168,8 @@ struct tid_ampdu_tx { ...@@ -168,6 +168,8 @@ struct tid_ampdu_tx {
* *
* @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
* A-MSDU with individually reported subframes. * A-MSDU with individually reported subframes.
* @reorder_buf_filtered: bitmap indicating where there are filtered frames in
* the reorder buffer that should be ignored when releasing frames
* @reorder_time: jiffies when skb was added * @reorder_time: jiffies when skb was added
* @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
* @reorder_timer: releases expired frames from the reorder buffer. * @reorder_timer: releases expired frames from the reorder buffer.
...@@ -195,6 +197,7 @@ struct tid_ampdu_tx { ...@@ -195,6 +197,7 @@ struct tid_ampdu_tx {
struct tid_ampdu_rx { struct tid_ampdu_rx {
struct rcu_head rcu_head; struct rcu_head rcu_head;
spinlock_t reorder_lock; spinlock_t reorder_lock;
u64 reorder_buf_filtered;
struct sk_buff_head *reorder_buf; struct sk_buff_head *reorder_buf;
unsigned long *reorder_time; unsigned long *reorder_time;
struct timer_list session_timer; struct timer_list session_timer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment