diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/driver-ops.h | 15 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 35 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 82 |
3 files changed, 116 insertions, 16 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4f845c0845ee..8fa0d2edf54c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -670,4 +670,19 @@ static inline void drv_rssi_callback(struct ieee80211_local *local, local->ops->rssi_callback(&local->hw, event); trace_drv_return_void(local); } + +static inline void +drv_release_buffered_frames(struct ieee80211_local *local, + struct sta_info *sta, u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + trace_drv_release_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->release_buffered_frames) + local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, + num_frames, reason, + more_data); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index a46b279bbbe4..531fbd086794 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1129,6 +1129,41 @@ TRACE_EVENT(drv_rssi_callback, ) ); +TRACE_EVENT(drv_release_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u16, tids) + __field(int, num_frames) + __field(int, reason) + __field(bool, more_data) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->tids = tids; + __entry->num_frames = num_frames; + __entry->reason = reason; + __entry->more_data = more_data; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT + " TIDs:0x%.4x frames:%d reason:%d more:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, + __entry->reason, __entry->more_data + ) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4d85672f0b8f..b3f841948c09 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1157,8 +1157,10 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff *skb = NULL; + bool found = false; bool more_data = false; int ac; + unsigned long driver_release_tids = 0; u8 ignore_for_response = sta->sta.uapsd_queues; /* @@ -1173,19 +1175,40 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * Get response frame and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + if (ignore_for_response & BIT(ac)) continue; - if (!skb) { - skb = skb_dequeue(&sta->tx_filtered[ac]); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf[ac]); + tids = ieee80211_tids_for_ac(ac); + + if (!found) { + driver_release_tids = sta->driver_buffered_tids & tids; + if (driver_release_tids) { + found = true; + } else { + 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) - local->total_ps_buffered--; + found = true; } - } - /* FIXME: take into account driver-buffered frames */ + /* + * If the driver has data on more than one TID then + * certainly there's more data if we release just a + * single frame now (from a single TID). + */ + if (hweight16(driver_release_tids) > 1) { + more_data = true; + driver_release_tids = + BIT(ffs(driver_release_tids) - 1); + break; + } + } if (!skb_queue_empty(&sta->tx_filtered[ac]) || !skb_queue_empty(&sta->ps_tx_buf[ac])) { @@ -1194,6 +1217,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) } } + if (!found) { +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + /* + * FIXME: This can be the result of a race condition between + * us expiring a frame and the station polling for it. + * Should we send it a null-func frame indicating we + * have nothing buffered for it? + */ + printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " + "though there are no buffered frames for it\n", + sdata->name, sta->sta.addr); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + + return; + } + if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = @@ -1220,18 +1259,29 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) ieee80211_add_pending_skb(local, skb); sta_info_recalc_tim(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG } else { /* - * FIXME: This can be the result of a race condition between - * us expiring a frame and the station polling for it. - * Should we send it a null-func frame indicating we - * have nothing buffered for it? + * We need to release a frame that is buffered somewhere in the + * driver ... it'll have to handle that. + * Note that, as per the comment above, it'll also have to see + * if there is more than just one frame on the specific TID that + * we're releasing from, and it needs to set the more-data bit + * accordingly if we tell it that there's no more data. If we do + * tell it there's more data, then of course the more-data bit + * needs to be set anyway. + */ + drv_release_buffered_frames(local, sta, driver_release_tids, + 1, IEEE80211_FRAME_RELEASE_PSPOLL, + more_data); + + /* + * 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 + * that the TID became empty before returning here from the + * release function. + * Either way, however, when the driver tells us that the TID + * became empty we'll do the TIM recalculation. */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } } |