aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/wme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-02-11 18:51:53 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-02-27 14:51:42 -0500
commit96f5e66e8a79810e2982cdcfa28e554f3d97da21 (patch)
treed16a0e083b83ab488f20b995c56a496a4ec2c9c8 /net/mac80211/wme.c
parentf3734ee6df3ac57151e02d091f47d5e52e646539 (diff)
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: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/wme.c')
-rw-r--r--net/mac80211/wme.c161
1 files changed, 4 insertions, 157 deletions
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index ac71b38f7cb5..093a4ab7f28b 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -114,9 +114,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
114{ 114{
115 struct ieee80211_master_priv *mpriv = netdev_priv(dev); 115 struct ieee80211_master_priv *mpriv = netdev_priv(dev);
116 struct ieee80211_local *local = mpriv->local; 116 struct ieee80211_local *local = mpriv->local;
117 struct ieee80211_hw *hw = &local->hw;
118 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 117 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
119 struct sta_info *sta;
120 u16 queue; 118 u16 queue;
121 u8 tid; 119 u8 tid;
122 120
@@ -124,29 +122,11 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
124 if (unlikely(queue >= local->hw.queues)) 122 if (unlikely(queue >= local->hw.queues))
125 queue = local->hw.queues - 1; 123 queue = local->hw.queues - 1;
126 124
127 if (skb->requeue) { 125 /*
128 if (!hw->ampdu_queues) 126 * Now we know the 1d priority, fill in the QoS header if
129 return queue; 127 * there is one (and we haven't done this before).
130
131 rcu_read_lock();
132 sta = sta_info_get(local, hdr->addr1);
133 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
134 if (sta) {
135 int ampdu_queue = sta->tid_to_tx_q[tid];
136
137 if ((ampdu_queue < ieee80211_num_queues(hw)) &&
138 test_bit(ampdu_queue, local->queue_pool))
139 queue = ampdu_queue;
140 }
141 rcu_read_unlock();
142
143 return queue;
144 }
145
146 /* Now we know the 1d priority, fill in the QoS header if
147 * there is one.
148 */ 128 */
149 if (ieee80211_is_data_qos(hdr->frame_control)) { 129 if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) {
150 u8 *p = ieee80211_get_qos_ctl(hdr); 130 u8 *p = ieee80211_get_qos_ctl(hdr);
151 u8 ack_policy = 0; 131 u8 ack_policy = 0;
152 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; 132 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 /* qos header is 2 bytes, second reserved */ 136 /* qos header is 2 bytes, second reserved */
157 *p++ = ack_policy | tid; 137 *p++ = ack_policy | tid;
158 *p = 0; 138 *p = 0;
159
160 if (!hw->ampdu_queues)
161 return queue;
162
163 rcu_read_lock();
164
165 sta = sta_info_get(local, hdr->addr1);
166 if (sta) {
167 int ampdu_queue = sta->tid_to_tx_q[tid];
168
169 if ((ampdu_queue < ieee80211_num_queues(hw)) &&
170 test_bit(ampdu_queue, local->queue_pool))
171 queue = ampdu_queue;
172 }
173
174 rcu_read_unlock();
175 } 139 }
176 140
177 return queue; 141 return queue;
178} 142}
179
180int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
181 struct sta_info *sta, u16 tid)
182{
183 int i;
184
185 /* XXX: currently broken due to cb/requeue use */
186 return -EPERM;
187
188 /* prepare the filter and save it for the SW queue
189 * matching the received HW queue */
190
191 if (!local->hw.ampdu_queues)
192 return -EPERM;
193
194 /* try to get a Qdisc from the pool */
195 for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++)
196 if (!test_and_set_bit(i, local->queue_pool)) {
197 ieee80211_stop_queue(local_to_hw(local), i);
198 sta->tid_to_tx_q[tid] = i;
199
200 /* IF there are already pending packets
201 * on this tid first we need to drain them
202 * on the previous queue
203 * since HT is strict in order */
204#ifdef CONFIG_MAC80211_HT_DEBUG
205 if (net_ratelimit())
206 printk(KERN_DEBUG "allocated aggregation queue"
207 " %d tid %d addr %pM pool=0x%lX\n",
208 i, tid, sta->sta.addr,
209 local->queue_pool[0]);
210#endif /* CONFIG_MAC80211_HT_DEBUG */
211 return 0;
212 }
213
214 return -EAGAIN;
215}
216
217/**
218 * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock
219 */
220void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
221 struct sta_info *sta, u16 tid,
222 u8 requeue)
223{
224 int agg_queue = sta->tid_to_tx_q[tid];
225 struct ieee80211_hw *hw = &local->hw;
226
227 /* return the qdisc to the pool */
228 clear_bit(agg_queue, local->queue_pool);
229 sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw);
230
231 if (requeue) {
232 ieee80211_requeue(local, agg_queue);
233 } else {
234 struct netdev_queue *txq;
235 spinlock_t *root_lock;
236 struct Qdisc *q;
237
238 txq = netdev_get_tx_queue(local->mdev, agg_queue);
239 q = rcu_dereference(txq->qdisc);
240 root_lock = qdisc_lock(q);
241
242 spin_lock_bh(root_lock);
243 qdisc_reset(q);
244 spin_unlock_bh(root_lock);
245 }
246}
247
248void ieee80211_requeue(struct ieee80211_local *local, int queue)
249{
250 struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue);
251 struct sk_buff_head list;
252 spinlock_t *root_lock;
253 struct Qdisc *qdisc;
254 u32 len;
255
256 rcu_read_lock_bh();
257
258 qdisc = rcu_dereference(txq->qdisc);
259 if (!qdisc || !qdisc->dequeue)
260 goto out_unlock;
261
262 skb_queue_head_init(&list);
263
264 root_lock = qdisc_root_lock(qdisc);
265 spin_lock(root_lock);
266 for (len = qdisc->q.qlen; len > 0; len--) {
267 struct sk_buff *skb = qdisc->dequeue(qdisc);
268
269 if (skb)
270 __skb_queue_tail(&list, skb);
271 }
272 spin_unlock(root_lock);
273
274 for (len = list.qlen; len > 0; len--) {
275 struct sk_buff *skb = __skb_dequeue(&list);
276 u16 new_queue;
277
278 BUG_ON(!skb);
279 new_queue = ieee80211_select_queue(local->mdev, skb);
280 skb_set_queue_mapping(skb, new_queue);
281
282 txq = netdev_get_tx_queue(local->mdev, new_queue);
283
284
285 qdisc = rcu_dereference(txq->qdisc);
286 root_lock = qdisc_root_lock(qdisc);
287
288 spin_lock(root_lock);
289 qdisc_enqueue_root(skb, qdisc);
290 spin_unlock(root_lock);
291 }
292
293out_unlock:
294 rcu_read_unlock_bh();
295}