a71748b3026e4d3b18f25c98ae27a6a15a5679b2
[openwrt/openwrt.git] /
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
4 fairness
5
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
10 list modifications.
11
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
13 ---
14
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.
20 *
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);
28
29 /**
30 - * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
31 - *
32 - * @hw: pointer as obtained from ieee80211_alloc_hw()
33 - * @txq: pointer obtained from station or virtual interface
34 - *
35 - * Should only be called between calls to ieee80211_txq_schedule_start()
36 - * and ieee80211_txq_schedule_end().
37 - */
38 -void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
39 -
40 -/**
41 - * ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
42 + * ieee80211_txq_schedule_start - start new scheduling round for TXQs
43 *
44 * @hw: pointer as obtained from ieee80211_alloc_hw()
45 * @ac: AC number to acquire locks for
46 *
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().
50 */
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);
54 +
55 +/* (deprecated) */
56 +static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
57 +{
58 +}
59
60 /**
61 - * ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
62 + * ieee80211_schedule_txq - schedule a TXQ for transmission
63 *
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
67 *
68 - * Release locks previously acquired by ieee80211_txq_schedule_end().
69 + * Schedules a TXQ for transmission if it is not already scheduled.
70 */
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);
74
75 /**
76 - * ieee80211_schedule_txq - schedule a TXQ for transmission
77 + * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
78 *
79 * @hw: pointer as obtained from ieee80211_alloc_hw()
80 * @txq: pointer obtained from station or virtual interface
81 - *
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()
85 */
86 -void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
87 - __acquires(txq_lock) __releases(txq_lock);
88 +static inline void
89 +ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
90 +{
91 + ieee80211_schedule_txq(hw, txq);
92 +}
93
94 /**
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 @@ -3637,16 +3637,17 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
99 struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
100 {
101 struct ieee80211_local *local = hw_to_local(hw);
102 + struct ieee80211_txq *ret = NULL;
103 struct txq_info *txqi = NULL;
104
105 - lockdep_assert_held(&local->active_txq_lock[ac]);
106 + spin_lock_bh(&local->active_txq_lock[ac]);
107
108 begin:
109 txqi = list_first_entry_or_null(&local->active_txqs[ac],
110 struct txq_info,
111 schedule_order);
112 if (!txqi)
113 - return NULL;
114 + goto out;
115
116 if (txqi->txq.sta) {
117 struct sta_info *sta = container_of(txqi->txq.sta,
118 @@ -3663,21 +3664,25 @@ struct ieee80211_txq *ieee80211_next_txq
119
120
121 if (txqi->schedule_round == local->schedule_round[ac])
122 - return NULL;
123 + goto out;
124
125 list_del_init(&txqi->schedule_order);
126 txqi->schedule_round = local->schedule_round[ac];
127 - return &txqi->txq;
128 + ret = &txqi->txq;
129 +
130 +out:
131 + spin_unlock_bh(&local->active_txq_lock[ac]);
132 + return ret;
133 }
134 EXPORT_SYMBOL(ieee80211_next_txq);
135
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)
140 {
141 struct ieee80211_local *local = hw_to_local(hw);
142 struct txq_info *txqi = to_txq_info(txq);
143
144 - lockdep_assert_held(&local->active_txq_lock[txq->ac]);
145 + spin_lock_bh(&local->active_txq_lock[txq->ac]);
146
147 if (list_empty(&txqi->schedule_order) &&
148 (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
149 @@ -3697,18 +3702,7 @@ void ieee80211_return_txq(struct ieee802
150 list_add_tail(&txqi->schedule_order,
151 &local->active_txqs[txq->ac]);
152 }
153 -}
154 -EXPORT_SYMBOL(ieee80211_return_txq);
155
156 -void ieee80211_schedule_txq(struct ieee80211_hw *hw,
157 - struct ieee80211_txq *txq)
158 - __acquires(txq_lock) __releases(txq_lock)
159 -{
160 - struct ieee80211_local *local = hw_to_local(hw);
161 - struct txq_info *txqi = to_txq_info(txq);
162 -
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]);
166 }
167 EXPORT_SYMBOL(ieee80211_schedule_txq);
168 @@ -3721,7 +3715,7 @@ bool ieee80211_txq_may_transmit(struct i
169 struct sta_info *sta;
170 u8 ac = txq->ac;
171
172 - lockdep_assert_held(&local->active_txq_lock[ac]);
173 + spin_lock_bh(&local->active_txq_lock[ac]);
174
175 if (!txqi->txq.sta)
176 goto out;
177 @@ -3751,34 +3745,27 @@ bool ieee80211_txq_may_transmit(struct i
178
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]);
182
183 return false;
184 out:
185 if (!list_empty(&txqi->schedule_order))
186 list_del_init(&txqi->schedule_order);
187 + spin_unlock_bh(&local->active_txq_lock[ac]);
188
189 return true;
190 }
191 EXPORT_SYMBOL(ieee80211_txq_may_transmit);
192
193 void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
194 - __acquires(txq_lock)
195 {
196 struct ieee80211_local *local = hw_to_local(hw);
197
198 spin_lock_bh(&local->active_txq_lock[ac]);
199 local->schedule_round[ac]++;
200 -}
201 -EXPORT_SYMBOL(ieee80211_txq_schedule_start);
202 -
203 -void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
204 - __releases(txq_lock)
205 -{
206 - struct ieee80211_local *local = hw_to_local(hw);
207 -
208 spin_unlock_bh(&local->active_txq_lock[ac]);
209 }
210 -EXPORT_SYMBOL(ieee80211_txq_schedule_end);
211 +EXPORT_SYMBOL(ieee80211_txq_schedule_start);
212
213 void __ieee80211_subif_start_xmit(struct sk_buff *skb,
214 struct net_device *dev,