]>
Commit | Line | Data |
---|---|---|
f0706e82 JB |
1 | /* |
2 | * Copyright 2004, Instant802 Networks, Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/netdevice.h> | |
10 | #include <linux/skbuff.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/if_arp.h> | |
13 | #include <linux/types.h> | |
14 | #include <net/ip.h> | |
15 | #include <net/pkt_sched.h> | |
16 | ||
17 | #include <net/mac80211.h> | |
18 | #include "ieee80211_i.h" | |
19 | #include "wme.h" | |
20 | ||
51cb6db0 | 21 | /* Default mapping in classifier to work with default |
e100bb64 JB |
22 | * queue setup. |
23 | */ | |
9e723492 | 24 | const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; |
f0706e82 | 25 | |
a8bdf29c | 26 | static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; |
f0706e82 | 27 | |
51cb6db0 DM |
28 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
29 | static unsigned int classify_1d(struct sk_buff *skb) | |
f0706e82 | 30 | { |
51cb6db0 | 31 | unsigned int dscp; |
f0706e82 JB |
32 | |
33 | /* skb->priority values from 256->263 are magic values to | |
51cb6db0 DM |
34 | * directly indicate a specific 802.1d priority. This is used |
35 | * to allow 802.1d priority to be passed directly in from VLAN | |
36 | * tags, etc. | |
37 | */ | |
f0706e82 JB |
38 | if (skb->priority >= 256 && skb->priority <= 263) |
39 | return skb->priority - 256; | |
40 | ||
51cb6db0 | 41 | switch (skb->protocol) { |
60678040 | 42 | case htons(ETH_P_IP): |
51cb6db0 DM |
43 | dscp = ip_hdr(skb)->tos & 0xfc; |
44 | break; | |
f0706e82 | 45 | |
51cb6db0 DM |
46 | default: |
47 | return 0; | |
48 | } | |
f0706e82 | 49 | |
f0706e82 JB |
50 | return dscp >> 5; |
51 | } | |
52 | ||
53 | ||
51cb6db0 | 54 | static int wme_downgrade_ac(struct sk_buff *skb) |
f0706e82 JB |
55 | { |
56 | switch (skb->priority) { | |
57 | case 6: | |
58 | case 7: | |
59 | skb->priority = 5; /* VO -> VI */ | |
60 | return 0; | |
61 | case 4: | |
62 | case 5: | |
63 | skb->priority = 3; /* VI -> BE */ | |
64 | return 0; | |
65 | case 0: | |
66 | case 3: | |
67 | skb->priority = 2; /* BE -> BK */ | |
68 | return 0; | |
69 | default: | |
70 | return -1; | |
71 | } | |
72 | } | |
73 | ||
74 | ||
51cb6db0 DM |
75 | /* Indicate which queue to use. */ |
76 | static u16 classify80211(struct sk_buff *skb, struct net_device *dev) | |
f0706e82 | 77 | { |
51cb6db0 | 78 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
f0706e82 | 79 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
f0706e82 | 80 | |
002aaf4e | 81 | if (!ieee80211_is_data(hdr->frame_control)) { |
f0706e82 JB |
82 | /* management frames go on AC_VO queue, but are sent |
83 | * without QoS control fields */ | |
e100bb64 | 84 | return 0; |
f0706e82 JB |
85 | } |
86 | ||
f9d540ee JB |
87 | if (0 /* injected */) { |
88 | /* use AC from radiotap */ | |
f0706e82 JB |
89 | } |
90 | ||
002aaf4e | 91 | if (!ieee80211_is_data_qos(hdr->frame_control)) { |
f0706e82 JB |
92 | skb->priority = 0; /* required for correct WPA/11i MIC */ |
93 | return ieee802_1d_to_ac[skb->priority]; | |
94 | } | |
95 | ||
96 | /* use the data classifier to determine what 802.1d tag the | |
3c3b00ca | 97 | * data frame has */ |
51cb6db0 | 98 | skb->priority = classify_1d(skb); |
f0706e82 | 99 | |
3c3b00ca | 100 | /* in case we are a client verify acm is not set for this ac */ |
f0706e82 JB |
101 | while (unlikely(local->wmm_acm & BIT(skb->priority))) { |
102 | if (wme_downgrade_ac(skb)) { | |
51cb6db0 DM |
103 | /* The old code would drop the packet in this |
104 | * case. | |
105 | */ | |
106 | return 0; | |
f0706e82 JB |
107 | } |
108 | } | |
109 | ||
110 | /* look up which queue to use for frames with this 1d tag */ | |
111 | return ieee802_1d_to_ac[skb->priority]; | |
112 | } | |
113 | ||
51cb6db0 | 114 | u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) |
f0706e82 | 115 | { |
f0706e82 | 116 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
51cb6db0 DM |
117 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
118 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | |
9e723492 | 119 | struct sta_info *sta; |
51cb6db0 | 120 | u16 queue; |
9e723492 | 121 | u8 tid; |
f0706e82 | 122 | |
51cb6db0 DM |
123 | queue = classify80211(skb, dev); |
124 | if (unlikely(queue >= local->hw.queues)) | |
125 | queue = local->hw.queues - 1; | |
126 | ||
e039fa4a | 127 | if (info->flags & IEEE80211_TX_CTL_REQUEUE) { |
d0709a65 | 128 | rcu_read_lock(); |
9e723492 | 129 | sta = sta_info_get(local, hdr->addr1); |
238f74a2 | 130 | tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; |
9e723492 | 131 | if (sta) { |
51cb6db0 | 132 | struct ieee80211_hw *hw = &local->hw; |
9e723492 | 133 | int ampdu_queue = sta->tid_to_tx_q[tid]; |
51cb6db0 DM |
134 | |
135 | if ((ampdu_queue < ieee80211_num_queues(hw)) && | |
136 | test_bit(ampdu_queue, local->queue_pool)) { | |
9e723492 | 137 | queue = ampdu_queue; |
e039fa4a | 138 | info->flags |= IEEE80211_TX_CTL_AMPDU; |
9e723492 | 139 | } else { |
e039fa4a | 140 | info->flags &= ~IEEE80211_TX_CTL_AMPDU; |
9e723492 | 141 | } |
9e723492 | 142 | } |
d0709a65 | 143 | rcu_read_unlock(); |
f0706e82 | 144 | |
51cb6db0 DM |
145 | return queue; |
146 | } | |
e100bb64 | 147 | |
51cb6db0 DM |
148 | /* Now we know the 1d priority, fill in the QoS header if |
149 | * there is one. | |
f0706e82 | 150 | */ |
002aaf4e HH |
151 | if (ieee80211_is_data_qos(hdr->frame_control)) { |
152 | u8 *p = ieee80211_get_qos_ctl(hdr); | |
9e723492 | 153 | u8 ack_policy = 0; |
238f74a2 | 154 | tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; |
f0706e82 | 155 | if (local->wifi_wme_noack_test) |
9e723492 | 156 | ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << |
f0706e82 JB |
157 | QOS_CONTROL_ACK_POLICY_SHIFT; |
158 | /* qos header is 2 bytes, second reserved */ | |
002aaf4e | 159 | *p++ = ack_policy | tid; |
f0706e82 | 160 | *p = 0; |
9e723492 | 161 | |
d0709a65 JB |
162 | rcu_read_lock(); |
163 | ||
9e723492 RR |
164 | sta = sta_info_get(local, hdr->addr1); |
165 | if (sta) { | |
166 | int ampdu_queue = sta->tid_to_tx_q[tid]; | |
51cb6db0 DM |
167 | struct ieee80211_hw *hw = &local->hw; |
168 | ||
169 | if ((ampdu_queue < ieee80211_num_queues(hw)) && | |
170 | test_bit(ampdu_queue, local->queue_pool)) { | |
9e723492 | 171 | queue = ampdu_queue; |
e039fa4a | 172 | info->flags |= IEEE80211_TX_CTL_AMPDU; |
9e723492 | 173 | } else { |
e039fa4a | 174 | info->flags &= ~IEEE80211_TX_CTL_AMPDU; |
9e723492 | 175 | } |
9e723492 | 176 | } |
d0709a65 JB |
177 | |
178 | rcu_read_unlock(); | |
f0706e82 JB |
179 | } |
180 | ||
f0706e82 JB |
181 | return queue; |
182 | } | |
183 | ||
9e723492 | 184 | int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, |
51cb6db0 | 185 | struct sta_info *sta, u16 tid) |
9e723492 RR |
186 | { |
187 | int i; | |
9e723492 | 188 | |
d0f09804 JB |
189 | /* XXX: currently broken due to cb/requeue use */ |
190 | return -EPERM; | |
191 | ||
9e723492 | 192 | /* prepare the filter and save it for the SW queue |
e100bb64 JB |
193 | * matching the received HW queue */ |
194 | ||
195 | if (!local->hw.ampdu_queues) | |
196 | return -EPERM; | |
9e723492 RR |
197 | |
198 | /* try to get a Qdisc from the pool */ | |
51cb6db0 DM |
199 | for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++) |
200 | if (!test_and_set_bit(i, local->queue_pool)) { | |
9e723492 RR |
201 | ieee80211_stop_queue(local_to_hw(local), i); |
202 | sta->tid_to_tx_q[tid] = i; | |
203 | ||
204 | /* IF there are already pending packets | |
205 | * on this tid first we need to drain them | |
206 | * on the previous queue | |
207 | * since HT is strict in order */ | |
208 | #ifdef CONFIG_MAC80211_HT_DEBUG | |
51cb6db0 DM |
209 | if (net_ratelimit()) { |
210 | DECLARE_MAC_BUF(mac); | |
9e723492 | 211 | printk(KERN_DEBUG "allocated aggregation queue" |
995ad6c5 | 212 | " %d tid %d addr %s pool=0x%lX\n", |
17741cdc | 213 | i, tid, print_mac(mac, sta->sta.addr), |
51cb6db0 DM |
214 | local->queue_pool[0]); |
215 | } | |
9e723492 RR |
216 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
217 | return 0; | |
218 | } | |
219 | ||
220 | return -EAGAIN; | |
221 | } | |
222 | ||
223 | /** | |
e8a0464c | 224 | * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock |
9e723492 RR |
225 | */ |
226 | void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, | |
227 | struct sta_info *sta, u16 tid, | |
228 | u8 requeue) | |
229 | { | |
9e723492 | 230 | int agg_queue = sta->tid_to_tx_q[tid]; |
51cb6db0 | 231 | struct ieee80211_hw *hw = &local->hw; |
9e723492 RR |
232 | |
233 | /* return the qdisc to the pool */ | |
51cb6db0 DM |
234 | clear_bit(agg_queue, local->queue_pool); |
235 | sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw); | |
9e723492 | 236 | |
51cb6db0 | 237 | if (requeue) { |
9e723492 | 238 | ieee80211_requeue(local, agg_queue); |
51cb6db0 DM |
239 | } else { |
240 | struct netdev_queue *txq; | |
83874000 | 241 | spinlock_t *root_lock; |
35ed4e75 | 242 | struct Qdisc *q; |
51cb6db0 DM |
243 | |
244 | txq = netdev_get_tx_queue(local->mdev, agg_queue); | |
35ed4e75 DM |
245 | q = rcu_dereference(txq->qdisc); |
246 | root_lock = qdisc_lock(q); | |
51cb6db0 | 247 | |
83874000 | 248 | spin_lock_bh(root_lock); |
35ed4e75 | 249 | qdisc_reset(q); |
83874000 | 250 | spin_unlock_bh(root_lock); |
51cb6db0 | 251 | } |
9e723492 RR |
252 | } |
253 | ||
254 | void ieee80211_requeue(struct ieee80211_local *local, int queue) | |
255 | { | |
51cb6db0 DM |
256 | struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue); |
257 | struct sk_buff_head list; | |
83874000 | 258 | spinlock_t *root_lock; |
51cb6db0 | 259 | struct Qdisc *qdisc; |
0da926f0 | 260 | u32 len; |
9e723492 | 261 | |
51cb6db0 DM |
262 | rcu_read_lock_bh(); |
263 | ||
264 | qdisc = rcu_dereference(txq->qdisc); | |
9e723492 | 265 | if (!qdisc || !qdisc->dequeue) |
51cb6db0 DM |
266 | goto out_unlock; |
267 | ||
268 | skb_queue_head_init(&list); | |
9e723492 | 269 | |
83874000 DM |
270 | root_lock = qdisc_root_lock(qdisc); |
271 | spin_lock(root_lock); | |
9e723492 | 272 | for (len = qdisc->q.qlen; len > 0; len--) { |
51cb6db0 DM |
273 | struct sk_buff *skb = qdisc->dequeue(qdisc); |
274 | ||
9e723492 | 275 | if (skb) |
51cb6db0 DM |
276 | __skb_queue_tail(&list, skb); |
277 | } | |
83874000 | 278 | spin_unlock(root_lock); |
51cb6db0 DM |
279 | |
280 | for (len = list.qlen; len > 0; len--) { | |
281 | struct sk_buff *skb = __skb_dequeue(&list); | |
282 | u16 new_queue; | |
283 | ||
284 | BUG_ON(!skb); | |
285 | new_queue = ieee80211_select_queue(local->mdev, skb); | |
286 | skb_set_queue_mapping(skb, new_queue); | |
287 | ||
288 | txq = netdev_get_tx_queue(local->mdev, new_queue); | |
289 | ||
51cb6db0 DM |
290 | |
291 | qdisc = rcu_dereference(txq->qdisc); | |
83874000 | 292 | root_lock = qdisc_root_lock(qdisc); |
51cb6db0 | 293 | |
83874000 | 294 | spin_lock(root_lock); |
5f86173b | 295 | qdisc_enqueue_root(skb, qdisc); |
83874000 | 296 | spin_unlock(root_lock); |
9e723492 | 297 | } |
51cb6db0 DM |
298 | |
299 | out_unlock: | |
300 | rcu_read_unlock_bh(); | |
9e723492 | 301 | } |