mac80211: rework locking for txq scheduling / airtime fairness
Holding the lock around the entire duration of tx scheduling can create some nasty lock contention, especially when processing airtime information from the tx status or the rx path. Improve locking by only holding the active_txq_lock for lookups / scheduling list modifications. Signed-off-by: Felix Fietkau <nbd@nbd.name> Acked-by: Toke Høiland-Jørgensen <toke@redhat.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
d6db02a88a
commit
5b989c18da
|
@ -6231,8 +6231,6 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
||||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||||
* @ac: AC number to return packets from.
|
* @ac: AC number to return packets from.
|
||||||
*
|
*
|
||||||
* Should only be called between calls to ieee80211_txq_schedule_start()
|
|
||||||
* and ieee80211_txq_schedule_end().
|
|
||||||
* Returns the next txq if successful, %NULL if no queue is eligible. If a txq
|
* Returns the next txq if successful, %NULL if no queue is eligible. If a txq
|
||||||
* is returned, it should be returned with ieee80211_return_txq() after the
|
* is returned, it should be returned with ieee80211_return_txq() after the
|
||||||
* driver has finished scheduling it.
|
* driver has finished scheduling it.
|
||||||
|
@ -6240,38 +6238,20 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
|
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
* ieee80211_txq_schedule_start - start new scheduling round for TXQs
|
||||||
*
|
|
||||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
|
||||||
* @txq: pointer obtained from station or virtual interface
|
|
||||||
*
|
|
||||||
* Should only be called between calls to ieee80211_txq_schedule_start()
|
|
||||||
* and ieee80211_txq_schedule_end().
|
|
||||||
*/
|
|
||||||
void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
|
|
||||||
*
|
*
|
||||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||||
* @ac: AC number to acquire locks for
|
* @ac: AC number to acquire locks for
|
||||||
*
|
*
|
||||||
* Acquire locks needed to schedule TXQs from the given AC. Should be called
|
* Should be called before ieee80211_next_txq() or ieee80211_return_txq().
|
||||||
* before ieee80211_next_txq() or ieee80211_return_txq().
|
* The driver must not call multiple TXQ scheduling rounds concurrently.
|
||||||
*/
|
*/
|
||||||
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac);
|
||||||
__acquires(txq_lock);
|
|
||||||
|
|
||||||
/**
|
/* (deprecated) */
|
||||||
* ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
|
static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||||
*
|
{
|
||||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
}
|
||||||
* @ac: AC number to acquire locks for
|
|
||||||
*
|
|
||||||
* Release locks previously acquired by ieee80211_txq_schedule_end().
|
|
||||||
*/
|
|
||||||
void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
|
||||||
__releases(txq_lock);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_schedule_txq - schedule a TXQ for transmission
|
* ieee80211_schedule_txq - schedule a TXQ for transmission
|
||||||
|
@ -6279,12 +6259,21 @@ void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||||
* @txq: pointer obtained from station or virtual interface
|
* @txq: pointer obtained from station or virtual interface
|
||||||
*
|
*
|
||||||
* Schedules a TXQ for transmission if it is not already scheduled. Takes a
|
* Schedules a TXQ for transmission if it is not already scheduled.
|
||||||
* lock, which means it must *not* be called between
|
|
||||||
* ieee80211_txq_schedule_start() and ieee80211_txq_schedule_end()
|
|
||||||
*/
|
*/
|
||||||
void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||||
__acquires(txq_lock) __releases(txq_lock);
|
|
||||||
|
/**
|
||||||
|
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
||||||
|
*
|
||||||
|
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||||
|
* @txq: pointer obtained from station or virtual interface
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||||
|
{
|
||||||
|
ieee80211_schedule_txq(hw, txq);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
||||||
|
|
|
@ -3649,16 +3649,17 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
|
||||||
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
struct ieee80211_txq *ret = NULL;
|
||||||
struct txq_info *txqi = NULL;
|
struct txq_info *txqi = NULL;
|
||||||
|
|
||||||
lockdep_assert_held(&local->active_txq_lock[ac]);
|
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||||
|
|
||||||
begin:
|
begin:
|
||||||
txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
||||||
struct txq_info,
|
struct txq_info,
|
||||||
schedule_order);
|
schedule_order);
|
||||||
if (!txqi)
|
if (!txqi)
|
||||||
return NULL;
|
goto out;
|
||||||
|
|
||||||
if (txqi->txq.sta) {
|
if (txqi->txq.sta) {
|
||||||
struct sta_info *sta = container_of(txqi->txq.sta,
|
struct sta_info *sta = container_of(txqi->txq.sta,
|
||||||
|
@ -3675,21 +3676,25 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
||||||
|
|
||||||
|
|
||||||
if (txqi->schedule_round == local->schedule_round[ac])
|
if (txqi->schedule_round == local->schedule_round[ac])
|
||||||
return NULL;
|
goto out;
|
||||||
|
|
||||||
list_del_init(&txqi->schedule_order);
|
list_del_init(&txqi->schedule_order);
|
||||||
txqi->schedule_round = local->schedule_round[ac];
|
txqi->schedule_round = local->schedule_round[ac];
|
||||||
return &txqi->txq;
|
ret = &txqi->txq;
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_next_txq);
|
EXPORT_SYMBOL(ieee80211_next_txq);
|
||||||
|
|
||||||
void ieee80211_return_txq(struct ieee80211_hw *hw,
|
void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_txq *txq)
|
struct ieee80211_txq *txq)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
struct txq_info *txqi = to_txq_info(txq);
|
struct txq_info *txqi = to_txq_info(txq);
|
||||||
|
|
||||||
lockdep_assert_held(&local->active_txq_lock[txq->ac]);
|
spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
||||||
|
|
||||||
if (list_empty(&txqi->schedule_order) &&
|
if (list_empty(&txqi->schedule_order) &&
|
||||||
(!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
|
(!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
|
||||||
|
@ -3709,17 +3714,7 @@ void ieee80211_return_txq(struct ieee80211_hw *hw,
|
||||||
list_add_tail(&txqi->schedule_order,
|
list_add_tail(&txqi->schedule_order,
|
||||||
&local->active_txqs[txq->ac]);
|
&local->active_txqs[txq->ac]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(ieee80211_return_txq);
|
|
||||||
|
|
||||||
void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
|
||||||
struct ieee80211_txq *txq)
|
|
||||||
__acquires(txq_lock) __releases(txq_lock)
|
|
||||||
{
|
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
|
||||||
|
|
||||||
spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
|
||||||
ieee80211_return_txq(hw, txq);
|
|
||||||
spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_schedule_txq);
|
EXPORT_SYMBOL(ieee80211_schedule_txq);
|
||||||
|
@ -3732,7 +3727,7 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||||
struct sta_info *sta;
|
struct sta_info *sta;
|
||||||
u8 ac = txq->ac;
|
u8 ac = txq->ac;
|
||||||
|
|
||||||
lockdep_assert_held(&local->active_txq_lock[ac]);
|
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||||
|
|
||||||
if (!txqi->txq.sta)
|
if (!txqi->txq.sta)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -3762,34 +3757,27 @@ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||||
|
|
||||||
sta->airtime[ac].deficit += sta->airtime_weight;
|
sta->airtime[ac].deficit += sta->airtime_weight;
|
||||||
list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
||||||
|
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
out:
|
out:
|
||||||
if (!list_empty(&txqi->schedule_order))
|
if (!list_empty(&txqi->schedule_order))
|
||||||
list_del_init(&txqi->schedule_order);
|
list_del_init(&txqi->schedule_order);
|
||||||
|
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_txq_may_transmit);
|
EXPORT_SYMBOL(ieee80211_txq_may_transmit);
|
||||||
|
|
||||||
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||||
__acquires(txq_lock)
|
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
|
||||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||||
local->schedule_round[ac]++;
|
local->schedule_round[ac]++;
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
|
||||||
|
|
||||||
void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
|
||||||
__releases(txq_lock)
|
|
||||||
{
|
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
|
||||||
|
|
||||||
spin_unlock_bh(&local->active_txq_lock[ac]);
|
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_txq_schedule_end);
|
EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
||||||
|
|
||||||
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
|
|
Loading…
Reference in New Issue