mac80211: release multiple ACs in uAPSD, fix more-data bug
When a response for PS-Poll or a uAPSD trigger frame is sent, the more-data bit should be set according to 802.11-2012 11.2.1.5 h), meaning that it should indicate more data on the relevant ACs (delivery-enabled or nondelivery-enabled for uAPSD or PS-Poll.) In, for example, the following scenario: * 1 frame on VO queue (either in driver or in mac80211) * at least 1 frame on VI queue (in the driver) * both VO/VI are delivery-enabled * uAPSD trigger frame received The more-data flag to the driver would not be set, even though it should be. While fixing this, I noticed that we should really release frames from multiple ACs where there's data buffered in the driver for the corresponding TIDs. To address all this, restructure the code a bit to consider all ACs if we only release driver frames or only buffered frames. This also addresses the more-data bug described above as now the TIDs will all be marked as released, so the driver will have to check the number of frames. While at it, clarify some code and comments and remove the found variable, replacing it with the appropriate sw/hw release check. Reported-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
0a1cb80975
commit
f9f760b488
|
@ -1245,7 +1245,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
bool found = false;
|
|
||||||
bool more_data = false;
|
bool more_data = false;
|
||||||
int ac;
|
int ac;
|
||||||
unsigned long driver_release_tids = 0;
|
unsigned long driver_release_tids = 0;
|
||||||
|
@ -1256,9 +1255,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
|
|
||||||
__skb_queue_head_init(&frames);
|
__skb_queue_head_init(&frames);
|
||||||
|
|
||||||
/*
|
/* Get response frame(s) and more data bit for the last one. */
|
||||||
* Get response frame(s) and more data bit for it.
|
|
||||||
*/
|
|
||||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||||
unsigned long tids;
|
unsigned long tids;
|
||||||
|
|
||||||
|
@ -1267,33 +1264,17 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
|
|
||||||
tids = ieee80211_tids_for_ac(ac);
|
tids = ieee80211_tids_for_ac(ac);
|
||||||
|
|
||||||
if (!found) {
|
/* if we already have frames from software, then we can't also
|
||||||
driver_release_tids = sta->driver_buffered_tids & tids;
|
* release from hardware queues
|
||||||
if (driver_release_tids) {
|
*/
|
||||||
found = true;
|
if (skb_queue_empty(&frames))
|
||||||
} else {
|
driver_release_tids |= sta->driver_buffered_tids & tids;
|
||||||
struct sk_buff *skb;
|
|
||||||
|
|
||||||
while (n_frames > 0) {
|
if (driver_release_tids) {
|
||||||
skb = skb_dequeue(&sta->tx_filtered[ac]);
|
/* If the driver has data on more than one TID then
|
||||||
if (!skb) {
|
|
||||||
skb = skb_dequeue(
|
|
||||||
&sta->ps_tx_buf[ac]);
|
|
||||||
if (skb)
|
|
||||||
local->total_ps_buffered--;
|
|
||||||
}
|
|
||||||
if (!skb)
|
|
||||||
break;
|
|
||||||
n_frames--;
|
|
||||||
found = true;
|
|
||||||
__skb_queue_tail(&frames, skb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the driver has data on more than one TID then
|
|
||||||
* certainly there's more data if we release just a
|
* certainly there's more data if we release just a
|
||||||
* single frame now (from a single TID).
|
* single frame now (from a single TID). This will
|
||||||
|
* only happen for PS-Poll.
|
||||||
*/
|
*/
|
||||||
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
|
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
|
||||||
hweight16(driver_release_tids) > 1) {
|
hweight16(driver_release_tids) > 1) {
|
||||||
|
@ -1303,8 +1284,28 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
driver_release_tids));
|
driver_release_tids));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
while (n_frames > 0) {
|
||||||
|
skb = skb_dequeue(&sta->tx_filtered[ac]);
|
||||||
|
if (!skb) {
|
||||||
|
skb = skb_dequeue(
|
||||||
|
&sta->ps_tx_buf[ac]);
|
||||||
|
if (skb)
|
||||||
|
local->total_ps_buffered--;
|
||||||
|
}
|
||||||
|
if (!skb)
|
||||||
|
break;
|
||||||
|
n_frames--;
|
||||||
|
__skb_queue_tail(&frames, skb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we have more frames buffered on this AC, then set the
|
||||||
|
* more-data bit and abort the loop since we can't send more
|
||||||
|
* data from other ACs before the buffered frames from this.
|
||||||
|
*/
|
||||||
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
|
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
|
||||||
!skb_queue_empty(&sta->ps_tx_buf[ac])) {
|
!skb_queue_empty(&sta->ps_tx_buf[ac])) {
|
||||||
more_data = true;
|
more_data = true;
|
||||||
|
@ -1312,7 +1313,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (skb_queue_empty(&frames) && !driver_release_tids) {
|
||||||
int tid;
|
int tid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1334,10 +1335,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
|
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
|
||||||
|
|
||||||
ieee80211_send_null_response(sdata, sta, tid, reason);
|
ieee80211_send_null_response(sdata, sta, tid, reason);
|
||||||
return;
|
} else if (!driver_release_tids) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!driver_release_tids) {
|
|
||||||
struct sk_buff_head pending;
|
struct sk_buff_head pending;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
|
@ -1403,12 +1401,12 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
/*
|
/*
|
||||||
* We need to release a frame that is buffered somewhere in the
|
* We need to release a frame that is buffered somewhere in the
|
||||||
* driver ... it'll have to handle that.
|
* driver ... it'll have to handle that.
|
||||||
* Note that, as per the comment above, it'll also have to see
|
* Note that the driver also has to check the number of frames
|
||||||
* if there is more than just one frame on the specific TID that
|
* on the TIDs we're releasing from - if there are more than
|
||||||
* we're releasing from, and it needs to set the more-data bit
|
* n_frames it has to set the more-data bit (if we didn't ask
|
||||||
* accordingly if we tell it that there's no more data. If we do
|
* it to set it anyway due to other buffered frames); if there
|
||||||
* tell it there's more data, then of course the more-data bit
|
* are fewer than n_frames it has to make sure to adjust that
|
||||||
* needs to be set anyway.
|
* to allow the service period to end properly.
|
||||||
*/
|
*/
|
||||||
drv_release_buffered_frames(local, sta, driver_release_tids,
|
drv_release_buffered_frames(local, sta, driver_release_tids,
|
||||||
n_frames, reason, more_data);
|
n_frames, reason, more_data);
|
||||||
|
@ -1416,9 +1414,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
|
||||||
/*
|
/*
|
||||||
* Note that we don't recalculate the TIM bit here as it would
|
* Note that we don't recalculate the TIM bit here as it would
|
||||||
* most likely have no effect at all unless the driver told us
|
* most likely have no effect at all unless the driver told us
|
||||||
* that the TID became empty before returning here from the
|
* that the TID(s) became empty before returning here from the
|
||||||
* release function.
|
* release function.
|
||||||
* Either way, however, when the driver tells us that the TID
|
* Either way, however, when the driver tells us that the TID(s)
|
||||||
* became empty we'll do the TIM recalculation.
|
* became empty we'll do the TIM recalculation.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue