Commit 96f5e66e authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: fix aggregation for hardware with ampdu queues

Hardware with AMPDU queues currently has broken aggregation.

This patch fixes it by making all A-MPDUs go over the regular AC queues,
but keeping track of the hardware queues in mac80211. As a first rough
version, it actually stops the AC queue for extended periods of time,
which can be removed by adding buffering internal to mac80211, but is
currently not a huge problem because people rarely use multiple TIDs
that are in the same AC (and iwlwifi currently doesn't operate as AP).

This is a short-term fix, my current medium-term plan, which I hope to
execute soon as well, but am not sure can finish before .30, looks like
this:
 1) rework the internal queuing layer in mac80211 that we use for
    fragments if the driver stopped queue in the middle of a fragmented
    frame to be able to queue more frames at once (rather than just a
    single frame with its fragments)
 2) instead of stopping the entire AC queue, queue up the frames in a
    per-station/per-TID queue during aggregation session initiation,
    when the session has come up take all those frames and put them
    onto the queue from 1)
 3) push the ampdu queue layer abstraction this patch introduces in
    mac80211 into the driver, and remove the virtual queue stuff from
    mac80211 again

This plan will probably also affect ath9k in that mac80211 queues the
frames instead of passing them down, even when there are no ampdu queues.
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent f3734ee6
...@@ -1022,11 +1022,6 @@ static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw) ...@@ -1022,11 +1022,6 @@ static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw)
return hw->queues; return hw->queues;
} }
static inline int ieee80211_num_queues(struct ieee80211_hw *hw)
{
return hw->queues + hw->ampdu_queues;
}
static inline struct ieee80211_rate * static inline struct ieee80211_rate *
ieee80211_get_tx_rate(const struct ieee80211_hw *hw, ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c) const struct ieee80211_tx_info *c)
......
This diff is collapsed.
...@@ -564,12 +564,10 @@ enum { ...@@ -564,12 +564,10 @@ enum {
enum queue_stop_reason { enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_DRIVER, IEEE80211_QUEUE_STOP_REASON_DRIVER,
IEEE80211_QUEUE_STOP_REASON_PS, IEEE80211_QUEUE_STOP_REASON_PS,
IEEE80211_QUEUE_STOP_REASON_CSA IEEE80211_QUEUE_STOP_REASON_CSA,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
}; };
/* maximum number of hardware queues we support. */
#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
struct ieee80211_master_priv { struct ieee80211_master_priv {
struct ieee80211_local *local; struct ieee80211_local *local;
}; };
...@@ -582,9 +580,15 @@ struct ieee80211_local { ...@@ -582,9 +580,15 @@ struct ieee80211_local {
const struct ieee80211_ops *ops; const struct ieee80211_ops *ops;
unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)]; /* AC queue corresponding to each AMPDU queue */
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES];
unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES];
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES +
IEEE80211_MAX_AMPDU_QUEUES];
/* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
spinlock_t queue_stop_reason_lock; spinlock_t queue_stop_reason_lock;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */ struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count; int open_count;
int monitors, cooked_mntrs; int monitors, cooked_mntrs;
...@@ -1042,6 +1046,10 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, ...@@ -1042,6 +1046,10 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason); enum queue_stop_reason reason);
void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason); enum queue_stop_reason reason);
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason);
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason);
#ifdef CONFIG_MAC80211_NOINLINE #ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline #define debug_noinline noinline
......
...@@ -705,7 +705,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -705,7 +705,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops) const struct ieee80211_ops *ops)
{ {
struct ieee80211_local *local; struct ieee80211_local *local;
int priv_size; int priv_size, i;
struct wiphy *wiphy; struct wiphy *wiphy;
/* Ensure 32-byte alignment of our private data and hw private data. /* Ensure 32-byte alignment of our private data and hw private data.
...@@ -779,6 +779,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -779,6 +779,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
setup_timer(&local->dynamic_ps_timer, setup_timer(&local->dynamic_ps_timer,
ieee80211_dynamic_ps_timer, (unsigned long) local); ieee80211_dynamic_ps_timer, (unsigned long) local);
for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++)
local->ampdu_ac_queue[i] = -1;
/* using an s8 won't work with more than that */
BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127);
sta_info_init(local); sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
...@@ -872,7 +877,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -872,7 +877,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
"wmaster%d", ieee80211_master_setup, "wmaster%d", ieee80211_master_setup,
ieee80211_num_queues(hw)); hw->queues);
if (!mdev) if (!mdev)
goto fail_mdev_alloc; goto fail_mdev_alloc;
......
...@@ -202,6 +202,18 @@ void sta_info_destroy(struct sta_info *sta) ...@@ -202,6 +202,18 @@ void sta_info_destroy(struct sta_info *sta)
/* Make sure timer won't free the tid_rx struct, see below */ /* Make sure timer won't free the tid_rx struct, see below */
if (tid_rx) if (tid_rx)
tid_rx->shutdown = true; tid_rx->shutdown = true;
/*
* The stop callback cannot find this station any more, but
* it didn't complete its work -- start the queue if necessary
*/
if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK &&
sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK &&
local->hw.ampdu_queues)
ieee80211_wake_queue_by_reason(&local->hw,
local->hw.queues + sta->tid_to_tx_q[i],
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
/* /*
...@@ -275,8 +287,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, ...@@ -275,8 +287,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
* enable session_timer's data differentiation. refer to * enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */ * sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i; sta->timer_to_tid[i] = i;
/* tid to tx queue: initialize according to HW (0 is valid) */ sta->tid_to_tx_q[i] = -1;
sta->tid_to_tx_q[i] = ieee80211_num_queues(&local->hw);
/* rx */ /* rx */
sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_rx[i] = NULL; sta->ampdu_mlme.tid_rx[i] = NULL;
......
...@@ -200,7 +200,7 @@ struct sta_ampdu_mlme { ...@@ -200,7 +200,7 @@ struct sta_ampdu_mlme {
* @tid_seq: per-TID sequence numbers for sending to this STA * @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers * @timer_to_tid: identity mapping to ID timers
* @tid_to_tx_q: map tid to tx queue * @tid_to_tx_q: map tid to tx queue (invalid == negative values)
* @llid: Local link ID * @llid: Local link ID
* @plid: Peer link ID * @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state * @reason: Cancel reason on PLINK_HOLDING state
...@@ -275,7 +275,7 @@ struct sta_info { ...@@ -275,7 +275,7 @@ struct sta_info {
*/ */
struct sta_ampdu_mlme ampdu_mlme; struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM]; u8 timer_to_tid[STA_TID_NUM];
u8 tid_to_tx_q[STA_TID_NUM]; s8 tid_to_tx_q[STA_TID_NUM];
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
/* /*
......
...@@ -876,7 +876,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) ...@@ -876,7 +876,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
return TX_CONTINUE; return TX_CONTINUE;
} }
/* actual transmit path */ /* actual transmit path */
/* /*
...@@ -1016,12 +1015,20 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, ...@@ -1016,12 +1015,20 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->sta = sta_info_get(local, hdr->addr1); tx->sta = sta_info_get(local, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) { if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
unsigned long flags;
qc = ieee80211_get_qos_ctl(hdr); qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK; tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
spin_lock_irqsave(&tx->sta->lock, flags);
state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
if (*state == HT_AGG_STATE_OPERATIONAL) if (*state == HT_AGG_STATE_OPERATIONAL) {
info->flags |= IEEE80211_TX_CTL_AMPDU; info->flags |= IEEE80211_TX_CTL_AMPDU;
if (local->hw.ampdu_queues)
skb_set_queue_mapping(
skb, tx->local->hw.queues +
tx->sta->tid_to_tx_q[tid]);
}
spin_unlock_irqrestore(&tx->sta->lock, flags);
} }
if (is_multicast_ether_addr(hdr->addr1)) { if (is_multicast_ether_addr(hdr->addr1)) {
...@@ -1085,7 +1092,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, ...@@ -1085,7 +1092,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
int ret, i; int ret, i;
if (skb) { if (skb) {
if (netif_subqueue_stopped(local->mdev, skb)) if (ieee80211_queue_stopped(&local->hw,
skb_get_queue_mapping(skb)))
return IEEE80211_TX_PENDING; return IEEE80211_TX_PENDING;
ret = local->ops->tx(local_to_hw(local), skb); ret = local->ops->tx(local_to_hw(local), skb);
...@@ -1101,8 +1109,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, ...@@ -1101,8 +1109,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
info = IEEE80211_SKB_CB(tx->extra_frag[i]); info = IEEE80211_SKB_CB(tx->extra_frag[i]);
info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT); IEEE80211_TX_CTL_FIRST_FRAGMENT);
if (netif_subqueue_stopped(local->mdev, if (ieee80211_queue_stopped(&local->hw,
tx->extra_frag[i])) skb_get_queue_mapping(tx->extra_frag[i])))
return IEEE80211_TX_FRAG_AGAIN; return IEEE80211_TX_FRAG_AGAIN;
ret = local->ops->tx(local_to_hw(local), ret = local->ops->tx(local_to_hw(local),
......
...@@ -344,15 +344,36 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, ...@@ -344,15 +344,36 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
/* we don't need to track ampdu queues */ if (queue >= hw->queues) {
if (queue < ieee80211_num_regular_queues(hw)) { if (local->ampdu_ac_queue[queue - hw->queues] < 0)
__clear_bit(reason, &local->queue_stop_reasons[queue]); return;
/*
* for virtual aggregation queues, we need to refcount the
* internal mac80211 disable (multiple times!), keep track of
* driver disable _and_ make sure the regular queue is
* actually enabled.
*/
if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
else
__clear_bit(reason, &local->queue_stop_reasons[queue]);
if (local->queue_stop_reasons[queue] != 0) if (local->queue_stop_reasons[queue] ||
/* someone still has this queue stopped */ local->amdpu_ac_stop_refcnt[queue - hw->queues])
return; return;
/* now go on to treat the corresponding regular queue */
queue = local->ampdu_ac_queue[queue - hw->queues];
reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
} }
__clear_bit(reason, &local->queue_stop_reasons[queue]);
if (local->queue_stop_reasons[queue] != 0)
/* someone still has this queue stopped */
return;
if (test_bit(queue, local->queues_pending)) { if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run); set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet); tasklet_schedule(&local->tx_pending_tasklet);
...@@ -361,8 +382,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, ...@@ -361,8 +382,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
} }
} }
static void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason) enum queue_stop_reason reason)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
unsigned long flags; unsigned long flags;
...@@ -384,15 +405,33 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, ...@@ -384,15 +405,33 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
/* we don't need to track ampdu queues */ if (queue >= hw->queues) {
if (queue < ieee80211_num_regular_queues(hw)) if (local->ampdu_ac_queue[queue - hw->queues] < 0)
__set_bit(reason, &local->queue_stop_reasons[queue]); return;
/*
* for virtual aggregation queues, we need to refcount the
* internal mac80211 disable (multiple times!), keep track of
* driver disable _and_ make sure the regular queue is
* actually enabled.
*/
if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
else
__set_bit(reason, &local->queue_stop_reasons[queue]);
/* now go on to treat the corresponding regular queue */
queue = local->ampdu_ac_queue[queue - hw->queues];
reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
}
__set_bit(reason, &local->queue_stop_reasons[queue]);
netif_stop_subqueue(local->mdev, queue); netif_stop_subqueue(local->mdev, queue);
} }
static void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason) enum queue_stop_reason reason)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
unsigned long flags; unsigned long flags;
...@@ -418,7 +457,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, ...@@ -418,7 +457,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags); spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
for (i = 0; i < ieee80211_num_queues(hw); i++) for (i = 0; i < hw->queues; i++)
__ieee80211_stop_queue(hw, i, reason); __ieee80211_stop_queue(hw, i, reason);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
...@@ -434,6 +473,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues); ...@@ -434,6 +473,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
unsigned long flags;
if (queue >= hw->queues) {
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
queue = local->ampdu_ac_queue[queue - hw->queues];
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
if (queue < 0)
return true;
}
return __netif_subqueue_stopped(local->mdev, queue); return __netif_subqueue_stopped(local->mdev, queue);
} }
EXPORT_SYMBOL(ieee80211_queue_stopped); EXPORT_SYMBOL(ieee80211_queue_stopped);
......
...@@ -114,9 +114,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) ...@@ -114,9 +114,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{ {
struct ieee80211_master_priv *mpriv = netdev_priv(dev); struct ieee80211_master_priv *mpriv = netdev_priv(dev);
struct ieee80211_local *local = mpriv->local; struct ieee80211_local *local = mpriv->local;
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta;
u16 queue; u16 queue;
u8 tid; u8 tid;
...@@ -124,29 +122,11 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) ...@@ -124,29 +122,11 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
if (unlikely(queue >= local->hw.queues)) if (unlikely(queue >= local->hw.queues))
queue = local->hw.queues - 1; queue = local->hw.queues - 1;
if (skb->requeue) { /*
if (!hw->ampdu_queues) * Now we know the 1d priority, fill in the QoS header if
return queue; * there is one (and we haven't done this before).
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
}
rcu_read_unlock();
return queue;
}
/* Now we know the 1d priority, fill in the QoS header if
* there is one.
*/ */
if (ieee80211_is_data_qos(hdr->frame_control)) { if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) {
u8 *p = ieee80211_get_qos_ctl(hdr); u8 *p = ieee80211_get_qos_ctl(hdr);
u8 ack_policy = 0; u8 ack_policy = 0;
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
...@@ -156,140 +136,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) ...@@ -156,140 +136,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
/* qos header is 2 bytes, second reserved */ /* qos header is 2 bytes, second reserved */
*p++ = ack_policy | tid; *p++ = ack_policy | tid;
*p = 0; *p = 0;
if (!hw->ampdu_queues)
return queue;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
}
rcu_read_unlock();
} }
return queue; return queue;
} }
int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
int i;
/* XXX: currently broken due to cb/requeue use */
return -EPERM;
/* prepare the filter and save it for the SW queue
* matching the received HW queue */
if (!local->hw.ampdu_queues)
return -EPERM;
/* try to get a Qdisc from the pool */
for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++)
if (!test_and_set_bit(i, local->queue_pool)) {
ieee80211_stop_queue(local_to_hw(local), i);
sta->tid_to_tx_q[tid] = i;
/* IF there are already pending packets
* on this tid first we need to drain them
* on the previous queue
* since HT is strict in order */
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "allocated aggregation queue"
" %d tid %d addr %pM pool=0x%lX\n",
i, tid, sta->sta.addr,
local->queue_pool[0]);
#endif /* CONFIG_MAC80211_HT_DEBUG */
return 0;
}
return -EAGAIN;
}
/**
* the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock
*/
void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
struct sta_info *sta, u16 tid,
u8 requeue)
{
int agg_queue = sta->tid_to_tx_q[tid];
struct ieee80211_hw *hw = &local->hw;
/* return the qdisc to the pool */
clear_bit(agg_queue, local->queue_pool);
sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw);
if (requeue) {
ieee80211_requeue(local, agg_queue);
} else {
struct netdev_queue *txq;
spinlock_t *root_lock;
struct Qdisc *q;
txq = netdev_get_tx_queue(local->mdev, agg_queue);
q = rcu_dereference(txq->qdisc);
root_lock = qdisc_lock(q);
spin_lock_bh(root_lock);
qdisc_reset(q);
spin_unlock_bh(root_lock);
}
}
void ieee80211_requeue(struct ieee80211_local *local, int queue)
{
struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue);
struct sk_buff_head list;
spinlock_t *root_lock;
struct Qdisc *qdisc;
u32 len;
rcu_read_lock_bh();
qdisc = rcu_dereference(txq->qdisc);
if (!qdisc || !qdisc->dequeue)
goto out_unlock;
skb_queue_head_init(&list);
root_lock = qdisc_root_lock(qdisc);
spin_lock(root_lock);
for (len = qdisc->q.qlen; len > 0; len--) {
struct sk_buff *skb = qdisc->dequeue(qdisc);
if (skb)
__skb_queue_tail(&list, skb);
}
spin_unlock(root_lock);
for (len = list.qlen; len > 0; len--) {
struct sk_buff *skb = __skb_dequeue(&list);
u16 new_queue;
BUG_ON(!skb);
new_queue = ieee80211_select_queue(local->mdev, skb);
skb_set_queue_mapping(skb, new_queue);
txq = netdev_get_tx_queue(local->mdev, new_queue);
qdisc = rcu_dereference(txq->qdisc);
root_lock = qdisc_root_lock(qdisc);
spin_lock(root_lock);
qdisc_enqueue_root(skb, qdisc);
spin_unlock(root_lock);
}
out_unlock:
rcu_read_unlock_bh();
}
...@@ -21,11 +21,5 @@ ...@@ -21,11 +21,5 @@
extern const int ieee802_1d_to_ac[8]; extern const int ieee802_1d_to_ac[8];
u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb); u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb);
int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
struct sta_info *sta, u16 tid);
void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
struct sta_info *sta, u16 tid,
u8 requeue);
void ieee80211_requeue(struct ieee80211_local *local, int queue);
#endif /* _WME_H */ #endif /* _WME_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment