mac80211: unify SW/offload remain-on-channel

Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.

This fixes a number of small issues with the ROC
implementation:
 * offloaded remain-on-channel couldn't be queued,
   now we can queue it as well, if needed
 * in iwlwifi (the only user) offloaded ROC is
   mutually exclusive with scanning, use the new
   queue to handle that case -- I expect that it
   will later depend on a HW flag

The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.

The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.

Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Johannes Berg
2012-06-05 14:28:42 +02:00
zatwierdzone przez John W. Linville
rodzic 196ac1c13d
commit 2eb278e083
10 zmienionych plików z 538 dodań i 760 usunięć

Wyświetl plik

@@ -520,36 +520,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
u64 cookie = (unsigned long)skb;
acked = info->flags & IEEE80211_TX_STAT_ACK;
if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
acked = info->flags & IEEE80211_TX_STAT_ACK;
ieee80211_is_qos_nullfunc(hdr->frame_control))
cfg80211_probe_status(skb->dev, hdr->addr1,
cookie, acked, GFP_ATOMIC);
} else {
struct ieee80211_work *wk;
rcu_read_lock();
list_for_each_entry_rcu(wk, &local->work_list, list) {
if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
continue;
if (wk->offchan_tx.frame != skb)
continue;
wk->offchan_tx.status = true;
break;
}
rcu_read_unlock();
if (local->hw_roc_skb_for_status == skb) {
cookie = local->hw_roc_cookie ^ 2;
local->hw_roc_skb_for_status = NULL;
}
else
cfg80211_mgmt_tx_status(
skb->dev, cookie, skb->data, skb->len,
!!(info->flags & IEEE80211_TX_STAT_ACK),
GFP_ATOMIC);
}
acked, GFP_ATOMIC);
}
if (unlikely(info->ack_frame_id)) {