Skip to content

Commit

Permalink
mac80211: fix aggregation to not require queue stop
Browse files Browse the repository at this point in the history
Instead of stopping the entire AC queue when enabling aggregation
(which was only done for hardware with aggregation queues) buffer
the packets for each station, and release them to the pending skb
queue once aggregation is turned on successfully.

We get a little more code, but it becomes conceptually simpler and
we can remove the entire virtual queue mechanism from mac80211 in
a follow-up patch.

This changes how mac80211 behaves towards drivers that support
aggregation but have no hardware queues -- those drivers will now
not be handed packets while the aggregation session is being
established, but only after it has been fully established.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
jmberg authored and linvjw committed Mar 28, 2009
1 parent a220858 commit cd8ffc8
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 78 deletions.
4 changes: 4 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ struct ieee80211_bss_conf {
* @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or
* set this flag in the driver; indicates that the rate control
* algorithm was used and should be notified of TX status
* @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
* used to indicate that a pending frame requires TX processing before
* it can be sent out.
*/
enum mac80211_tx_control_flags {
IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
Expand All @@ -264,6 +267,7 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
IEEE80211_TX_INTFL_RCALGO = BIT(13),
IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14),
};

/**
Expand Down
136 changes: 84 additions & 52 deletions net/mac80211/agg-tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
state = &sta->ampdu_mlme.tid_state_tx[tid];

if (local->hw.ampdu_queues) {
if (initiator) {
/*
* Stop the AC queue to avoid issues where we send
* unaggregated frames already before the delba.
*/
ieee80211_stop_queue_by_reason(&local->hw,
local->hw.queues + sta->tid_to_tx_q[tid],
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}

/*
* Pretend the driver woke the queue, just in case
* it disabled it before the session was stopped.
Expand All @@ -158,6 +148,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
/* HW shall not deny going back to legacy */
if (WARN_ON(ret)) {
*state = HT_AGG_STATE_OPERATIONAL;
/*
* We may have pending packets get stuck in this case...
* Not bothering with a workaround for now.
*/
}

return ret;
Expand Down Expand Up @@ -226,13 +220,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */

if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "rejecting on voice AC\n");
#endif
return -EINVAL;
}

rcu_read_lock();

sta = sta_info_get(local, ra);
Expand Down Expand Up @@ -267,6 +254,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}

spin_lock_bh(&sta->lock);
spin_lock(&local->ampdu_lock);

sdata = sta->sdata;

Expand Down Expand Up @@ -308,21 +296,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ret = -ENOSPC;
goto err_unlock_sta;
}

/*
* If we successfully allocate the session, we can't have
* anything going on on the queue this TID maps into, so
* stop it for now. This is a "virtual" stop using the same
* mechanism that drivers will use.
*
* XXX: queue up frames for this session in the sta_info
* struct instead to avoid hitting all other STAs.
*/
ieee80211_stop_queue_by_reason(
&local->hw, hw->queues + qn,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}

/*
* While we're asking the driver about the aggregation,
* stop the AC queue so that we don't have to worry
* about frames that came in while we were doing that,
* which would require us to put them to the AC pending
* afterwards which just makes the code more complex.
*/
ieee80211_stop_queue_by_reason(
&local->hw, ieee80211_ac_from_tid(tid),
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);

/* prepare A-MPDU MLME for Tx aggregation */
sta->ampdu_mlme.tid_tx[tid] =
kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
Expand All @@ -336,6 +322,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
goto err_return_queue;
}

skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending);

/* Tx timer */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
sta_addba_resp_timer_expired;
Expand All @@ -362,6 +350,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
sta->tid_to_tx_q[tid] = qn;

/* Driver vetoed or OKed, but we can take packets again now */
ieee80211_wake_queue_by_reason(
&local->hw, ieee80211_ac_from_tid(tid),
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);

spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);

/* send an addBA request */
Expand All @@ -388,38 +382,79 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
sta->ampdu_mlme.tid_tx[tid] = NULL;
err_return_queue:
if (qn >= 0) {
/* We failed, so start queue again right away. */
ieee80211_wake_queue_by_reason(hw, hw->queues + qn,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
/* give queue back to pool */
spin_lock(&local->queue_stop_reason_lock);
local->ampdu_ac_queue[qn] = -1;
spin_unlock(&local->queue_stop_reason_lock);
}
ieee80211_wake_queue_by_reason(
&local->hw, ieee80211_ac_from_tid(tid),
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
err_unlock_sta:
spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);
unlock:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);

/*
* splice packets from the STA's pending to the local pending,
* requires a call to ieee80211_agg_splice_finish and holding
* local->ampdu_lock across both calls.
*/
static void ieee80211_agg_splice_packets(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
unsigned long flags;
u16 queue = ieee80211_ac_from_tid(tid);

ieee80211_stop_queue_by_reason(
&local->hw, queue,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);

if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) {
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
/* mark queue as pending, it is stopped already */
__set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
&local->queue_stop_reasons[queue]);
/* copy over remaining packets */
skb_queue_splice_tail_init(
&sta->ampdu_mlme.tid_tx[tid]->pending,
&local->pending[queue]);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}
}

static void ieee80211_agg_splice_finish(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
u16 queue = ieee80211_ac_from_tid(tid);

ieee80211_wake_queue_by_reason(
&local->hw, queue,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}

/* caller must hold sta->lock */
static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif

if (local->hw.ampdu_queues) {
/*
* Wake up the A-MPDU queue, we stopped it earlier,
* this will in turn wake the entire AC.
*/
ieee80211_wake_queue_by_reason(&local->hw,
local->hw.queues + sta->tid_to_tx_q[tid],
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}
spin_lock(&local->ampdu_lock);
ieee80211_agg_splice_packets(local, sta, tid);
/*
* NB: we rely on sta->lock being taken in the TX
* processing here when adding to the pending queue,
* otherwise we could only change the state of the
* session to OPERATIONAL _here_.
*/
ieee80211_agg_splice_finish(local, sta, tid);
spin_unlock(&local->ampdu_lock);

local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL,
&sta->sta, tid, NULL);
Expand Down Expand Up @@ -602,22 +637,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);

spin_lock_bh(&sta->lock);
spin_lock(&local->ampdu_lock);

if (*state & HT_AGG_STATE_INITIATOR_MSK &&
hw->ampdu_queues) {
/*
* Wake up this queue, we stopped it earlier,
* this will in turn wake the entire AC.
*/
ieee80211_wake_queue_by_reason(hw,
hw->queues + sta->tid_to_tx_q[tid],
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}
ieee80211_agg_splice_packets(local, sta, tid);

*state = HT_AGG_STATE_IDLE;
/* from now on packets are no longer put onto sta->pending */
sta->ampdu_mlme.addba_req_num[tid] = 0;
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;

ieee80211_agg_splice_finish(local, sta, tid);

spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);

rcu_read_unlock();
Expand Down
8 changes: 8 additions & 0 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,14 @@ struct ieee80211_local {
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;

/*
* This lock is used to prevent concurrent A-MPDU
* session start/stop processing, this thus also
* synchronises the ->ampdu_action() callback to
* drivers and limits it to one at a time.
*/
spinlock_t ampdu_lock;

/* number of interfaces with corresponding IFF_ flags */
atomic_t iff_allmultis, iff_promiscs;

Expand Down
2 changes: 2 additions & 0 deletions net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable);

spin_lock_init(&local->ampdu_lock);

return local_to_hw(local);
}
EXPORT_SYMBOL(ieee80211_alloc_hw);
Expand Down
5 changes: 5 additions & 0 deletions net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ void sta_info_destroy(struct sta_info *sta)
tid_tx = sta->ampdu_mlme.tid_tx[i];
if (tid_tx) {
del_timer_sync(&tid_tx->addba_resp_timer);
/*
* STA removed while aggregation session being
* started? Bit odd, but purge frames anyway.
*/
skb_queue_purge(&tid_tx->pending);
kfree(tid_tx);
}
}
Expand Down
2 changes: 2 additions & 0 deletions net/mac80211/sta_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,13 @@ enum ieee80211_sta_info_flags {
* struct tid_ampdu_tx - TID aggregation information (Tx).
*
* @addba_resp_timer: timer for peer's response to addba request
* @pending: pending frames queue -- use sta's spinlock to protect
* @ssn: Starting Sequence Number expected to be aggregated.
* @dialog_token: dialog token for aggregation session
*/
struct tid_ampdu_tx {
struct timer_list addba_resp_timer;
struct sk_buff_head pending;
u16 ssn;
u8 dialog_token;
};
Expand Down
Loading

0 comments on commit cd8ffc8

Please sign in to comment.