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 @@   * Copyright 2005-2006, Devicescape Software, Inc.   * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>   * Copyright 2007, Michael Wu <flamingice@sourmilk.net> - * Copyright 2007-2008, Intel Corporation + * Copyright 2007-2010, Intel Corporation   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ +/** + * DOC: RX A-MPDU aggregation + * + * Aggregation on the RX side requires only implementing the + * @ampdu_action callback that is invoked to start/stop any + * block-ack sessions for RX aggregation. + * + * When RX aggregation is started by the peer, the driver is + * notified via @ampdu_action function, with the + * %IEEE80211_AMPDU_RX_START action, and may reject the request + * in which case a negative response is sent to the peer, if it + * accepts it a positive response is sent. + * + * While the session is active, the device/driver are required + * to de-aggregate frames and pass them up one by one to mac80211, + * which will handle the reorder buffer. + * + * When the aggregation session is stopped again by the peer or + * ourselves, the driver's @ampdu_action function will be called + * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the + * call must not fail. + */ +  #include <linux/ieee80211.h>  #include <linux/slab.h>  #include <net/mac80211.h>  #include "ieee80211_i.h"  #include "driver-ops.h" -static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, -					    u16 initiator, u16 reason, -					    bool from_timer) +static void ieee80211_free_tid_rx(struct rcu_head *h)  { -	struct ieee80211_local *local = sta->local; -	struct tid_ampdu_rx *tid_rx; +	struct tid_ampdu_rx *tid_rx = +		container_of(h, struct tid_ampdu_rx, rcu_head);  	int i; -	spin_lock_bh(&sta->lock); +	for (i = 0; i < tid_rx->buf_size; i++) +		dev_kfree_skb(tid_rx->reorder_buf[i]); +	kfree(tid_rx->reorder_buf); +	kfree(tid_rx->reorder_time); +	kfree(tid_rx); +} -	/* check if TID is in operational state */ -	if (!sta->ampdu_mlme.tid_active_rx[tid]) { -		spin_unlock_bh(&sta->lock); -		return; -	} +void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, +				     u16 initiator, u16 reason) +{ +	struct ieee80211_local *local = sta->local; +	struct tid_ampdu_rx *tid_rx; -	sta->ampdu_mlme.tid_active_rx[tid] = false; +	lockdep_assert_held(&sta->ampdu_mlme.mtx);  	tid_rx = sta->ampdu_mlme.tid_rx[tid]; +	if (!tid_rx) +		return; + +	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL); +  #ifdef CONFIG_MAC80211_HT_DEBUG  	printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",  	       sta->sta.addr, tid); @@ -54,32 +85,17 @@ static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  		ieee80211_send_delba(sta->sdata, sta->sta.addr,  				     tid, 0, reason); -	/* free the reordering buffer */ -	for (i = 0; i < tid_rx->buf_size; i++) { -		if (tid_rx->reorder_buf[i]) { -			/* release the reordered frames */ -			dev_kfree_skb(tid_rx->reorder_buf[i]); -			tid_rx->stored_mpdu_num--; -			tid_rx->reorder_buf[i] = NULL; -		} -	} - -	/* free resources */ -	kfree(tid_rx->reorder_buf); -	kfree(tid_rx->reorder_time); -	sta->ampdu_mlme.tid_rx[tid] = NULL; - -	spin_unlock_bh(&sta->lock); +	del_timer_sync(&tid_rx->session_timer); -	if (!from_timer) -		del_timer_sync(&tid_rx->session_timer); -	kfree(tid_rx); +	call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);  }  void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  				    u16 initiator, u16 reason)  { -	___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, false); +	mutex_lock(&sta->ampdu_mlme.mtx); +	___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason); +	mutex_unlock(&sta->ampdu_mlme.mtx);  }  /* @@ -100,8 +116,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)  #ifdef CONFIG_MAC80211_HT_DEBUG  	printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);  #endif -	___ieee80211_stop_rx_ba_session(sta, *ptid, WLAN_BACK_RECIPIENT, -					WLAN_REASON_QSTA_TIMEOUT, true); +	set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); +	ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);  }  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,  	/* examine state machine */ -	spin_lock_bh(&sta->lock); +	mutex_lock(&sta->ampdu_mlme.mtx); -	if (sta->ampdu_mlme.tid_active_rx[tid]) { +	if (sta->ampdu_mlme.tid_rx[tid]) {  #ifdef CONFIG_MAC80211_HT_DEBUG  		if (net_ratelimit())  			printk(KERN_DEBUG "unexpected AddBA Req from " @@ -225,9 +241,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	}  	/* prepare A-MPDU MLME for Rx aggregation */ -	sta->ampdu_mlme.tid_rx[tid] = -			kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); -	if (!sta->ampdu_mlme.tid_rx[tid]) { +	tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); +	if (!tid_agg_rx) {  #ifdef CONFIG_MAC80211_HT_DEBUG  		if (net_ratelimit())  			printk(KERN_ERR "allocate rx mlme to tid %d failed\n", @@ -235,14 +250,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  #endif  		goto end;  	} -	/* rx timer */ -	sta->ampdu_mlme.tid_rx[tid]->session_timer.function = -				sta_rx_agg_session_timer_expired; -	sta->ampdu_mlme.tid_rx[tid]->session_timer.data = -				(unsigned long)&sta->timer_to_tid[tid]; -	init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer); -	tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; +	/* rx timer */ +	tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; +	tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; +	init_timer(&tid_agg_rx->session_timer);  	/* prepare reordering buffer */  	tid_agg_rx->reorder_buf = @@ -257,8 +269,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  #endif  		kfree(tid_agg_rx->reorder_buf);  		kfree(tid_agg_rx->reorder_time); -		kfree(sta->ampdu_mlme.tid_rx[tid]); -		sta->ampdu_mlme.tid_rx[tid] = NULL; +		kfree(tid_agg_rx);  		goto end;  	} @@ -270,13 +281,12 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	if (ret) {  		kfree(tid_agg_rx->reorder_buf); +		kfree(tid_agg_rx->reorder_time);  		kfree(tid_agg_rx); -		sta->ampdu_mlme.tid_rx[tid] = NULL;  		goto end;  	} -	/* change state and send addba resp */ -	sta->ampdu_mlme.tid_active_rx[tid] = true; +	/* update data */  	tid_agg_rx->dialog_token = dialog_token;  	tid_agg_rx->ssn = start_seq_num;  	tid_agg_rx->head_seq_num = start_seq_num; @@ -284,8 +294,15 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	tid_agg_rx->timeout = timeout;  	tid_agg_rx->stored_mpdu_num = 0;  	status = WLAN_STATUS_SUCCESS; + +	/* activate it for RX */ +	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx); + +	if (timeout) +		mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout)); +  end: -	spin_unlock_bh(&sta->lock); +	mutex_unlock(&sta->ampdu_mlme.mtx);  end_no_lock:  	ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,  |