1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Wed, 13 Mar 2019 19:09:22 +0100
3 Subject: [PATCH] mac80211: rework locking for txq scheduling / airtime
6 Holding the lock around the entire duration of tx scheduling can create
7 some nasty lock contention, especially when processing airtime information
8 from the tx status or the rx path.
9 Improve locking by only holding the active_txq_lock for lookups / scheduling
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
15 --- a/include/net/mac80211.h
16 +++ b/include/net/mac80211.h
17 @@ -6069,8 +6069,6 @@ struct sk_buff *ieee80211_tx_dequeue(str
18 * @hw: pointer as obtained from ieee80211_alloc_hw()
19 * @ac: AC number to return packets from.
21 - * Should only be called between calls to ieee80211_txq_schedule_start()
22 - * and ieee80211_txq_schedule_end().
23 * Returns the next txq if successful, %NULL if no queue is eligible. If a txq
24 * is returned, it should be returned with ieee80211_return_txq() after the
25 * driver has finished scheduling it.
26 @@ -6078,51 +6076,41 @@ struct sk_buff *ieee80211_tx_dequeue(str
27 struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
30 - * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
32 - * @hw: pointer as obtained from ieee80211_alloc_hw()
33 - * @txq: pointer obtained from station or virtual interface
35 - * Should only be called between calls to ieee80211_txq_schedule_start()
36 - * and ieee80211_txq_schedule_end().
38 -void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
41 - * ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
42 + * ieee80211_txq_schedule_start - start new scheduling round for TXQs
44 * @hw: pointer as obtained from ieee80211_alloc_hw()
45 * @ac: AC number to acquire locks for
47 - * Acquire locks needed to schedule TXQs from the given AC. Should be called
48 - * before ieee80211_next_txq() or ieee80211_return_txq().
49 + * Should be called before ieee80211_next_txq() or ieee80211_return_txq().
51 -void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
52 - __acquires(txq_lock);
53 +void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac);
56 +static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
61 - * ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
62 + * ieee80211_schedule_txq - schedule a TXQ for transmission
64 * @hw: pointer as obtained from ieee80211_alloc_hw()
65 - * @ac: AC number to acquire locks for
66 + * @txq: pointer obtained from station or virtual interface
68 - * Release locks previously acquired by ieee80211_txq_schedule_end().
69 + * Schedules a TXQ for transmission if it is not already scheduled.
71 -void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
72 - __releases(txq_lock);
73 +void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
76 - * ieee80211_schedule_txq - schedule a TXQ for transmission
77 + * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
79 * @hw: pointer as obtained from ieee80211_alloc_hw()
80 * @txq: pointer obtained from station or virtual interface
82 - * Schedules a TXQ for transmission if it is not already scheduled. Takes a
83 - * lock, which means it must *not* be called between
84 - * ieee80211_txq_schedule_start() and ieee80211_txq_schedule_end()
86 -void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
87 - __acquires(txq_lock) __releases(txq_lock);
89 +ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
91 + ieee80211_schedule_txq(hw, txq);
95 * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
96 --- a/net/mac80211/tx.c
97 +++ b/net/mac80211/tx.c
98 @@ -3656,16 +3656,17 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
99 struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
101 struct ieee80211_local *local = hw_to_local(hw);
102 + struct ieee80211_txq *ret = NULL;
103 struct txq_info *txqi = NULL;
105 - lockdep_assert_held(&local->active_txq_lock[ac]);
106 + spin_lock_bh(&local->active_txq_lock[ac]);
109 txqi = list_first_entry_or_null(&local->active_txqs[ac],
117 struct sta_info *sta = container_of(txqi->txq.sta,
118 @@ -3682,21 +3683,25 @@ struct ieee80211_txq *ieee80211_next_txq
121 if (txqi->schedule_round == local->schedule_round[ac])
125 list_del_init(&txqi->schedule_order);
126 txqi->schedule_round = local->schedule_round[ac];
131 + spin_unlock_bh(&local->active_txq_lock[ac]);
134 EXPORT_SYMBOL(ieee80211_next_txq);
136 -void ieee80211_return_txq(struct ieee80211_hw *hw,
137 - struct ieee80211_txq *txq)
138 +void ieee80211_schedule_txq(struct ieee80211_hw *hw,
139 + struct ieee80211_txq *txq)
141 struct ieee80211_local *local = hw_to_local(hw);
142 struct txq_info *txqi = to_txq_info(txq);
144 - lockdep_assert_held(&local->active_txq_lock[txq->ac]);
145 + spin_lock_bh(&local->active_txq_lock[txq->ac]);
147 if (list_empty(&txqi->schedule_order) &&
148 (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
149 @@ -3716,18 +3721,7 @@ void ieee80211_return_txq(struct ieee802
150 list_add_tail(&txqi->schedule_order,
151 &local->active_txqs[txq->ac]);
154 -EXPORT_SYMBOL(ieee80211_return_txq);
156 -void ieee80211_schedule_txq(struct ieee80211_hw *hw,
157 - struct ieee80211_txq *txq)
158 - __acquires(txq_lock) __releases(txq_lock)
160 - struct ieee80211_local *local = hw_to_local(hw);
161 - struct txq_info *txqi = to_txq_info(txq);
163 - spin_lock_bh(&local->active_txq_lock[txq->ac]);
164 - ieee80211_return_txq(hw, txq);
165 spin_unlock_bh(&local->active_txq_lock[txq->ac]);
167 EXPORT_SYMBOL(ieee80211_schedule_txq);
168 @@ -3740,7 +3734,7 @@ bool ieee80211_txq_may_transmit(struct i
169 struct sta_info *sta;
172 - lockdep_assert_held(&local->active_txq_lock[ac]);
173 + spin_lock_bh(&local->active_txq_lock[ac]);
177 @@ -3770,34 +3764,27 @@ bool ieee80211_txq_may_transmit(struct i
179 sta->airtime[ac].deficit += sta->airtime_weight;
180 list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
181 + spin_unlock_bh(&local->active_txq_lock[ac]);
185 if (!list_empty(&txqi->schedule_order))
186 list_del_init(&txqi->schedule_order);
187 + spin_unlock_bh(&local->active_txq_lock[ac]);
191 EXPORT_SYMBOL(ieee80211_txq_may_transmit);
193 void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
194 - __acquires(txq_lock)
196 struct ieee80211_local *local = hw_to_local(hw);
198 spin_lock_bh(&local->active_txq_lock[ac]);
199 local->schedule_round[ac]++;
201 -EXPORT_SYMBOL(ieee80211_txq_schedule_start);
203 -void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
204 - __releases(txq_lock)
206 - struct ieee80211_local *local = hw_to_local(hw);
208 spin_unlock_bh(&local->active_txq_lock[ac]);
210 -EXPORT_SYMBOL(ieee80211_txq_schedule_end);
211 +EXPORT_SYMBOL(ieee80211_txq_schedule_start);
213 void __ieee80211_subif_start_xmit(struct sk_buff *skb,
214 struct net_device *dev,