diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-02-02 10:58:06 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-02-02 16:38:59 -0500 |
commit | 4334ec8518cec3f7a4feeb3dacb46acfb24904d4 (patch) | |
tree | e00b87af0dc841a5bd2cf2af4fc0de7adeeaaff7 /net | |
parent | ff458edc0c5ec42b299547fb7eb9790a4aecc632 (diff) |
mac80211: fix TX status cookie in HW offload case
When the off-channel TX is done with remain-on-channel
offloaded to hardware, the reported cookie is wrong as
in that case we shouldn't use the SKB as the cookie but
need to instead use the corresponding r-o-c cookie
(XOR'ed with 2 to prevent API mismatches).
Fix this by keeping track of the hw_roc_skb pointer
just for the status processing and use the correct
cookie to report in this case. We can't use the
hw_roc_skb pointer itself because it is NULL'ed when
the frame is transmitted to prevent it being used
twice.
This fixes a bug where the P2P state machine in the
supplicant gets stuck because it never gets a correct
result for its transmitted frame.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/status.c | 7 |
3 files changed, 9 insertions, 2 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4bc8a9250cfd..9cd73b11506e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1822,6 +1822,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
1822 | *cookie ^= 2; | 1822 | *cookie ^= 2; |
1823 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; | 1823 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; |
1824 | local->hw_roc_skb = skb; | 1824 | local->hw_roc_skb = skb; |
1825 | local->hw_roc_skb_for_status = skb; | ||
1825 | mutex_unlock(&local->mtx); | 1826 | mutex_unlock(&local->mtx); |
1826 | 1827 | ||
1827 | return 0; | 1828 | return 0; |
@@ -1875,6 +1876,7 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, | |||
1875 | if (ret == 0) { | 1876 | if (ret == 0) { |
1876 | kfree_skb(local->hw_roc_skb); | 1877 | kfree_skb(local->hw_roc_skb); |
1877 | local->hw_roc_skb = NULL; | 1878 | local->hw_roc_skb = NULL; |
1879 | local->hw_roc_skb_for_status = NULL; | ||
1878 | } | 1880 | } |
1879 | 1881 | ||
1880 | mutex_unlock(&local->mtx); | 1882 | mutex_unlock(&local->mtx); |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c47d7c0e48a4..533fd32f49ff 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -953,7 +953,7 @@ struct ieee80211_local { | |||
953 | 953 | ||
954 | struct ieee80211_channel *hw_roc_channel; | 954 | struct ieee80211_channel *hw_roc_channel; |
955 | struct net_device *hw_roc_dev; | 955 | struct net_device *hw_roc_dev; |
956 | struct sk_buff *hw_roc_skb; | 956 | struct sk_buff *hw_roc_skb, *hw_roc_skb_for_status; |
957 | struct work_struct hw_roc_start, hw_roc_done; | 957 | struct work_struct hw_roc_start, hw_roc_done; |
958 | enum nl80211_channel_type hw_roc_channel_type; | 958 | enum nl80211_channel_type hw_roc_channel_type; |
959 | unsigned int hw_roc_duration; | 959 | unsigned int hw_roc_duration; |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 38a797217a91..071ac95c4aa0 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -323,6 +323,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
323 | 323 | ||
324 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { | 324 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { |
325 | struct ieee80211_work *wk; | 325 | struct ieee80211_work *wk; |
326 | u64 cookie = (unsigned long)skb; | ||
326 | 327 | ||
327 | rcu_read_lock(); | 328 | rcu_read_lock(); |
328 | list_for_each_entry_rcu(wk, &local->work_list, list) { | 329 | list_for_each_entry_rcu(wk, &local->work_list, list) { |
@@ -334,8 +335,12 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
334 | break; | 335 | break; |
335 | } | 336 | } |
336 | rcu_read_unlock(); | 337 | rcu_read_unlock(); |
338 | if (local->hw_roc_skb_for_status == skb) { | ||
339 | cookie = local->hw_roc_cookie ^ 2; | ||
340 | local->hw_roc_skb_for_status = NULL; | ||
341 | } | ||
337 | cfg80211_mgmt_tx_status( | 342 | cfg80211_mgmt_tx_status( |
338 | skb->dev, (unsigned long) skb, skb->data, skb->len, | 343 | skb->dev, cookie, skb->data, skb->len, |
339 | !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); | 344 | !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); |
340 | } | 345 | } |
341 | 346 | ||