Commit f9f760b4 authored by Johannes Berg's avatar Johannes Berg

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: default avatarEliad Peller <eliad@wizery.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 0a1cb809
...@@ -1245,7 +1245,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ...@@ -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, ...@@ -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, ...@@ -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) {
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--;
found = true;
__skb_queue_tail(&frames, skb);
}
}
/* if (driver_release_tids) {
* If the driver has data on more than one TID then /* 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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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.
*/ */
} }
......
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