diff options
Diffstat (limited to 'net/mac80211/agg-rx.c')
-rw-r--r-- | net/mac80211/agg-rx.c | 123 |
1 files changed, 70 insertions, 53 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 6bb9a9a9496..965b272499f 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c | |||
@@ -6,39 +6,70 @@ | |||
6 | * Copyright 2005-2006, Devicescape Software, Inc. | 6 | * Copyright 2005-2006, Devicescape Software, Inc. |
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
9 | * Copyright 2007-2008, Intel Corporation | 9 | * Copyright 2007-2010, Intel Corporation |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
13 | * published by the Free Software Foundation. | 13 | * published by the Free Software Foundation. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /** | ||
17 | * DOC: RX A-MPDU aggregation | ||
18 | * | ||
19 | * Aggregation on the RX side requires only implementing the | ||
20 | * @ampdu_action callback that is invoked to start/stop any | ||
21 | * block-ack sessions for RX aggregation. | ||
22 | * | ||
23 | * When RX aggregation is started by the peer, the driver is | ||
24 | * notified via @ampdu_action function, with the | ||
25 | * %IEEE80211_AMPDU_RX_START action, and may reject the request | ||
26 | * in which case a negative response is sent to the peer, if it | ||
27 | * accepts it a positive response is sent. | ||
28 | * | ||
29 | * While the session is active, the device/driver are required | ||
30 | * to de-aggregate frames and pass them up one by one to mac80211, | ||
31 | * which will handle the reorder buffer. | ||
32 | * | ||
33 | * When the aggregation session is stopped again by the peer or | ||
34 | * ourselves, the driver's @ampdu_action function will be called | ||
35 | * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the | ||
36 | * call must not fail. | ||
37 | */ | ||
38 | |||
16 | #include <linux/ieee80211.h> | 39 | #include <linux/ieee80211.h> |
17 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
18 | #include <net/mac80211.h> | 41 | #include <net/mac80211.h> |
19 | #include "ieee80211_i.h" | 42 | #include "ieee80211_i.h" |
20 | #include "driver-ops.h" | 43 | #include "driver-ops.h" |
21 | 44 | ||
22 | static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | 45 | static void ieee80211_free_tid_rx(struct rcu_head *h) |
23 | u16 initiator, u16 reason, | ||
24 | bool from_timer) | ||
25 | { | 46 | { |
26 | struct ieee80211_local *local = sta->local; | 47 | struct tid_ampdu_rx *tid_rx = |
27 | struct tid_ampdu_rx *tid_rx; | 48 | container_of(h, struct tid_ampdu_rx, rcu_head); |
28 | int i; | 49 | int i; |
29 | 50 | ||
30 | spin_lock_bh(&sta->lock); | 51 | for (i = 0; i < tid_rx->buf_size; i++) |
52 | dev_kfree_skb(tid_rx->reorder_buf[i]); | ||
53 | kfree(tid_rx->reorder_buf); | ||
54 | kfree(tid_rx->reorder_time); | ||
55 | kfree(tid_rx); | ||
56 | } | ||
31 | 57 | ||
32 | /* check if TID is in operational state */ | 58 | void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, |
33 | if (!sta->ampdu_mlme.tid_active_rx[tid]) { | 59 | u16 initiator, u16 reason) |
34 | spin_unlock_bh(&sta->lock); | 60 | { |
35 | return; | 61 | struct ieee80211_local *local = sta->local; |
36 | } | 62 | struct tid_ampdu_rx *tid_rx; |
37 | 63 | ||
38 | sta->ampdu_mlme.tid_active_rx[tid] = false; | 64 | lockdep_assert_held(&sta->ampdu_mlme.mtx); |
39 | 65 | ||
40 | tid_rx = sta->ampdu_mlme.tid_rx[tid]; | 66 | tid_rx = sta->ampdu_mlme.tid_rx[tid]; |
41 | 67 | ||
68 | if (!tid_rx) | ||
69 | return; | ||
70 | |||
71 | rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL); | ||
72 | |||
42 | #ifdef CONFIG_MAC80211_HT_DEBUG | 73 | #ifdef CONFIG_MAC80211_HT_DEBUG |
43 | printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", | 74 | printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", |
44 | sta->sta.addr, tid); | 75 | sta->sta.addr, tid); |
@@ -54,32 +85,17 @@ static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | |||
54 | ieee80211_send_delba(sta->sdata, sta->sta.addr, | 85 | ieee80211_send_delba(sta->sdata, sta->sta.addr, |
55 | tid, 0, reason); | 86 | tid, 0, reason); |
56 | 87 | ||
57 | /* free the reordering buffer */ | 88 | del_timer_sync(&tid_rx->session_timer); |
58 | for (i = 0; i < tid_rx->buf_size; i++) { | ||
59 | if (tid_rx->reorder_buf[i]) { | ||
60 | /* release the reordered frames */ | ||
61 | dev_kfree_skb(tid_rx->reorder_buf[i]); | ||
62 | tid_rx->stored_mpdu_num--; | ||
63 | tid_rx->reorder_buf[i] = NULL; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | /* free resources */ | ||
68 | kfree(tid_rx->reorder_buf); | ||
69 | kfree(tid_rx->reorder_time); | ||
70 | sta->ampdu_mlme.tid_rx[tid] = NULL; | ||
71 | |||
72 | spin_unlock_bh(&sta->lock); | ||
73 | 89 | ||
74 | if (!from_timer) | 90 | call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); |
75 | del_timer_sync(&tid_rx->session_timer); | ||
76 | kfree(tid_rx); | ||
77 | } | 91 | } |
78 | 92 | ||
79 | void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | 93 | void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, |
80 | u16 initiator, u16 reason) | 94 | u16 initiator, u16 reason) |
81 | { | 95 | { |
82 | ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, false); | 96 | mutex_lock(&sta->ampdu_mlme.mtx); |
97 | ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason); | ||
98 | mutex_unlock(&sta->ampdu_mlme.mtx); | ||
83 | } | 99 | } |
84 | 100 | ||
85 | /* | 101 | /* |
@@ -100,8 +116,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) | |||
100 | #ifdef CONFIG_MAC80211_HT_DEBUG | 116 | #ifdef CONFIG_MAC80211_HT_DEBUG |
101 | printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); | 117 | printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); |
102 | #endif | 118 | #endif |
103 | ___ieee80211_stop_rx_ba_session(sta, *ptid, WLAN_BACK_RECIPIENT, | 119 | set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); |
104 | WLAN_REASON_QSTA_TIMEOUT, true); | 120 | ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); |
105 | } | 121 | } |
106 | 122 | ||
107 | static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, | 123 | static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, |
@@ -212,9 +228,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
212 | 228 | ||
213 | 229 | ||
214 | /* examine state machine */ | 230 | /* examine state machine */ |
215 | spin_lock_bh(&sta->lock); | 231 | mutex_lock(&sta->ampdu_mlme.mtx); |
216 | 232 | ||
217 | if (sta->ampdu_mlme.tid_active_rx[tid]) { | 233 | if (sta->ampdu_mlme.tid_rx[tid]) { |
218 | #ifdef CONFIG_MAC80211_HT_DEBUG | 234 | #ifdef CONFIG_MAC80211_HT_DEBUG |
219 | if (net_ratelimit()) | 235 | if (net_ratelimit()) |
220 | printk(KERN_DEBUG "unexpected AddBA Req from " | 236 | printk(KERN_DEBUG "unexpected AddBA Req from " |
@@ -225,9 +241,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
225 | } | 241 | } |
226 | 242 | ||
227 | /* prepare A-MPDU MLME for Rx aggregation */ | 243 | /* prepare A-MPDU MLME for Rx aggregation */ |
228 | sta->ampdu_mlme.tid_rx[tid] = | 244 | tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); |
229 | kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); | 245 | if (!tid_agg_rx) { |
230 | if (!sta->ampdu_mlme.tid_rx[tid]) { | ||
231 | #ifdef CONFIG_MAC80211_HT_DEBUG | 246 | #ifdef CONFIG_MAC80211_HT_DEBUG |
232 | if (net_ratelimit()) | 247 | if (net_ratelimit()) |
233 | printk(KERN_ERR "allocate rx mlme to tid %d failed\n", | 248 | printk(KERN_ERR "allocate rx mlme to tid %d failed\n", |
@@ -235,14 +250,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
235 | #endif | 250 | #endif |
236 | goto end; | 251 | goto end; |
237 | } | 252 | } |
238 | /* rx timer */ | ||
239 | sta->ampdu_mlme.tid_rx[tid]->session_timer.function = | ||
240 | sta_rx_agg_session_timer_expired; | ||
241 | sta->ampdu_mlme.tid_rx[tid]->session_timer.data = | ||
242 | (unsigned long)&sta->timer_to_tid[tid]; | ||
243 | init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer); | ||
244 | 253 | ||
245 | tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; | 254 | /* rx timer */ |
255 | tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; | ||
256 | tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; | ||
257 | init_timer(&tid_agg_rx->session_timer); | ||
246 | 258 | ||
247 | /* prepare reordering buffer */ | 259 | /* prepare reordering buffer */ |
248 | tid_agg_rx->reorder_buf = | 260 | tid_agg_rx->reorder_buf = |
@@ -257,8 +269,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
257 | #endif | 269 | #endif |
258 | kfree(tid_agg_rx->reorder_buf); | 270 | kfree(tid_agg_rx->reorder_buf); |
259 | kfree(tid_agg_rx->reorder_time); | 271 | kfree(tid_agg_rx->reorder_time); |
260 | kfree(sta->ampdu_mlme.tid_rx[tid]); | 272 | kfree(tid_agg_rx); |
261 | sta->ampdu_mlme.tid_rx[tid] = NULL; | ||
262 | goto end; | 273 | goto end; |
263 | } | 274 | } |
264 | 275 | ||
@@ -270,13 +281,12 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
270 | 281 | ||
271 | if (ret) { | 282 | if (ret) { |
272 | kfree(tid_agg_rx->reorder_buf); | 283 | kfree(tid_agg_rx->reorder_buf); |
284 | kfree(tid_agg_rx->reorder_time); | ||
273 | kfree(tid_agg_rx); | 285 | kfree(tid_agg_rx); |
274 | sta->ampdu_mlme.tid_rx[tid] = NULL; | ||
275 | goto end; | 286 | goto end; |
276 | } | 287 | } |
277 | 288 | ||
278 | /* change state and send addba resp */ | 289 | /* update data */ |
279 | sta->ampdu_mlme.tid_active_rx[tid] = true; | ||
280 | tid_agg_rx->dialog_token = dialog_token; | 290 | tid_agg_rx->dialog_token = dialog_token; |
281 | tid_agg_rx->ssn = start_seq_num; | 291 | tid_agg_rx->ssn = start_seq_num; |
282 | tid_agg_rx->head_seq_num = start_seq_num; | 292 | tid_agg_rx->head_seq_num = start_seq_num; |
@@ -284,8 +294,15 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
284 | tid_agg_rx->timeout = timeout; | 294 | tid_agg_rx->timeout = timeout; |
285 | tid_agg_rx->stored_mpdu_num = 0; | 295 | tid_agg_rx->stored_mpdu_num = 0; |
286 | status = WLAN_STATUS_SUCCESS; | 296 | status = WLAN_STATUS_SUCCESS; |
297 | |||
298 | /* activate it for RX */ | ||
299 | rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx); | ||
300 | |||
301 | if (timeout) | ||
302 | mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout)); | ||
303 | |||
287 | end: | 304 | end: |
288 | spin_unlock_bh(&sta->lock); | 305 | mutex_unlock(&sta->ampdu_mlme.mtx); |
289 | 306 | ||
290 | end_no_lock: | 307 | end_no_lock: |
291 | ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, | 308 | ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, |