diff options
Diffstat (limited to 'net/mac80211/wme.c')
-rw-r--r-- | net/mac80211/wme.c | 139 |
1 files changed, 129 insertions, 10 deletions
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 4e236599dd31..4e94e4026e78 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c | |||
@@ -19,10 +19,13 @@ | |||
19 | #include "wme.h" | 19 | #include "wme.h" |
20 | 20 | ||
21 | /* maximum number of hardware queues we support. */ | 21 | /* maximum number of hardware queues we support. */ |
22 | #define TC_80211_MAX_QUEUES 8 | 22 | #define TC_80211_MAX_QUEUES 16 |
23 | |||
24 | const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; | ||
23 | 25 | ||
24 | struct ieee80211_sched_data | 26 | struct ieee80211_sched_data |
25 | { | 27 | { |
28 | unsigned long qdisc_pool[BITS_TO_LONGS(TC_80211_MAX_QUEUES)]; | ||
26 | struct tcf_proto *filter_list; | 29 | struct tcf_proto *filter_list; |
27 | struct Qdisc *queues[TC_80211_MAX_QUEUES]; | 30 | struct Qdisc *queues[TC_80211_MAX_QUEUES]; |
28 | struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; | 31 | struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; |
@@ -98,7 +101,6 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) | |||
98 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 101 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
99 | unsigned short fc = le16_to_cpu(hdr->frame_control); | 102 | unsigned short fc = le16_to_cpu(hdr->frame_control); |
100 | int qos; | 103 | int qos; |
101 | const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; | ||
102 | 104 | ||
103 | /* see if frame is data or non data frame */ | 105 | /* see if frame is data or non data frame */ |
104 | if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) { | 106 | if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) { |
@@ -146,9 +148,26 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) | |||
146 | unsigned short fc = le16_to_cpu(hdr->frame_control); | 148 | unsigned short fc = le16_to_cpu(hdr->frame_control); |
147 | struct Qdisc *qdisc; | 149 | struct Qdisc *qdisc; |
148 | int err, queue; | 150 | int err, queue; |
151 | struct sta_info *sta; | ||
152 | u8 tid; | ||
149 | 153 | ||
150 | if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) { | 154 | if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) { |
151 | skb_queue_tail(&q->requeued[pkt_data->queue], skb); | 155 | queue = pkt_data->queue; |
156 | rcu_read_lock(); | ||
157 | sta = sta_info_get(local, hdr->addr1); | ||
158 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
159 | if (sta) { | ||
160 | int ampdu_queue = sta->tid_to_tx_q[tid]; | ||
161 | if ((ampdu_queue < local->hw.queues) && | ||
162 | test_bit(ampdu_queue, q->qdisc_pool)) { | ||
163 | queue = ampdu_queue; | ||
164 | pkt_data->flags |= IEEE80211_TXPD_AMPDU; | ||
165 | } else { | ||
166 | pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; | ||
167 | } | ||
168 | } | ||
169 | rcu_read_unlock(); | ||
170 | skb_queue_tail(&q->requeued[queue], skb); | ||
152 | qd->q.qlen++; | 171 | qd->q.qlen++; |
153 | return 0; | 172 | return 0; |
154 | } | 173 | } |
@@ -159,14 +178,31 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) | |||
159 | */ | 178 | */ |
160 | if (WLAN_FC_IS_QOS_DATA(fc)) { | 179 | if (WLAN_FC_IS_QOS_DATA(fc)) { |
161 | u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; | 180 | u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; |
162 | u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK; | 181 | u8 ack_policy = 0; |
182 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
163 | if (local->wifi_wme_noack_test) | 183 | if (local->wifi_wme_noack_test) |
164 | qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK << | 184 | ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << |
165 | QOS_CONTROL_ACK_POLICY_SHIFT; | 185 | QOS_CONTROL_ACK_POLICY_SHIFT; |
166 | /* qos header is 2 bytes, second reserved */ | 186 | /* qos header is 2 bytes, second reserved */ |
167 | *p = qos_hdr; | 187 | *p = ack_policy | tid; |
168 | p++; | 188 | p++; |
169 | *p = 0; | 189 | *p = 0; |
190 | |||
191 | rcu_read_lock(); | ||
192 | |||
193 | sta = sta_info_get(local, hdr->addr1); | ||
194 | if (sta) { | ||
195 | int ampdu_queue = sta->tid_to_tx_q[tid]; | ||
196 | if ((ampdu_queue < local->hw.queues) && | ||
197 | test_bit(ampdu_queue, q->qdisc_pool)) { | ||
198 | queue = ampdu_queue; | ||
199 | pkt_data->flags |= IEEE80211_TXPD_AMPDU; | ||
200 | } else { | ||
201 | pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | rcu_read_unlock(); | ||
170 | } | 206 | } |
171 | 207 | ||
172 | if (unlikely(queue >= local->hw.queues)) { | 208 | if (unlikely(queue >= local->hw.queues)) { |
@@ -184,6 +220,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) | |||
184 | kfree_skb(skb); | 220 | kfree_skb(skb); |
185 | err = NET_XMIT_DROP; | 221 | err = NET_XMIT_DROP; |
186 | } else { | 222 | } else { |
223 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
187 | pkt_data->queue = (unsigned int) queue; | 224 | pkt_data->queue = (unsigned int) queue; |
188 | qdisc = q->queues[queue]; | 225 | qdisc = q->queues[queue]; |
189 | err = qdisc->enqueue(skb, qdisc); | 226 | err = qdisc->enqueue(skb, qdisc); |
@@ -235,10 +272,11 @@ static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) | |||
235 | /* check all the h/w queues in numeric/priority order */ | 272 | /* check all the h/w queues in numeric/priority order */ |
236 | for (queue = 0; queue < hw->queues; queue++) { | 273 | for (queue = 0; queue < hw->queues; queue++) { |
237 | /* see if there is room in this hardware queue */ | 274 | /* see if there is room in this hardware queue */ |
238 | if (test_bit(IEEE80211_LINK_STATE_XOFF, | 275 | if ((test_bit(IEEE80211_LINK_STATE_XOFF, |
239 | &local->state[queue]) || | 276 | &local->state[queue])) || |
240 | test_bit(IEEE80211_LINK_STATE_PENDING, | 277 | (test_bit(IEEE80211_LINK_STATE_PENDING, |
241 | &local->state[queue])) | 278 | &local->state[queue])) || |
279 | (!test_bit(queue, q->qdisc_pool))) | ||
242 | continue; | 280 | continue; |
243 | 281 | ||
244 | /* there is space - try and get a frame */ | 282 | /* there is space - try and get a frame */ |
@@ -360,6 +398,10 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) | |||
360 | } | 398 | } |
361 | } | 399 | } |
362 | 400 | ||
401 | /* reserve all legacy QoS queues */ | ||
402 | for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++) | ||
403 | set_bit(i, q->qdisc_pool); | ||
404 | |||
363 | return err; | 405 | return err; |
364 | } | 406 | } |
365 | 407 | ||
@@ -605,3 +647,80 @@ void ieee80211_wme_unregister(void) | |||
605 | { | 647 | { |
606 | unregister_qdisc(&wme_qdisc_ops); | 648 | unregister_qdisc(&wme_qdisc_ops); |
607 | } | 649 | } |
650 | |||
651 | int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, | ||
652 | struct sta_info *sta, u16 tid) | ||
653 | { | ||
654 | int i; | ||
655 | struct ieee80211_sched_data *q = | ||
656 | qdisc_priv(local->mdev->qdisc_sleeping); | ||
657 | DECLARE_MAC_BUF(mac); | ||
658 | |||
659 | /* prepare the filter and save it for the SW queue | ||
660 | * matching the recieved HW queue */ | ||
661 | |||
662 | /* try to get a Qdisc from the pool */ | ||
663 | for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++) | ||
664 | if (!test_and_set_bit(i, q->qdisc_pool)) { | ||
665 | ieee80211_stop_queue(local_to_hw(local), i); | ||
666 | sta->tid_to_tx_q[tid] = i; | ||
667 | |||
668 | /* IF there are already pending packets | ||
669 | * on this tid first we need to drain them | ||
670 | * on the previous queue | ||
671 | * since HT is strict in order */ | ||
672 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
673 | if (net_ratelimit()) | ||
674 | printk(KERN_DEBUG "allocated aggregation queue" | ||
675 | " %d tid %d addr %s pool=0x%lX", | ||
676 | i, tid, print_mac(mac, sta->addr), | ||
677 | q->qdisc_pool[0]); | ||
678 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | return -EAGAIN; | ||
683 | } | ||
684 | |||
685 | /** | ||
686 | * the caller needs to hold local->mdev->queue_lock | ||
687 | */ | ||
688 | void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, | ||
689 | struct sta_info *sta, u16 tid, | ||
690 | u8 requeue) | ||
691 | { | ||
692 | struct ieee80211_sched_data *q = | ||
693 | qdisc_priv(local->mdev->qdisc_sleeping); | ||
694 | int agg_queue = sta->tid_to_tx_q[tid]; | ||
695 | |||
696 | /* return the qdisc to the pool */ | ||
697 | clear_bit(agg_queue, q->qdisc_pool); | ||
698 | sta->tid_to_tx_q[tid] = local->hw.queues; | ||
699 | |||
700 | if (requeue) | ||
701 | ieee80211_requeue(local, agg_queue); | ||
702 | else | ||
703 | q->queues[agg_queue]->ops->reset(q->queues[agg_queue]); | ||
704 | } | ||
705 | |||
706 | void ieee80211_requeue(struct ieee80211_local *local, int queue) | ||
707 | { | ||
708 | struct Qdisc *root_qd = local->mdev->qdisc_sleeping; | ||
709 | struct ieee80211_sched_data *q = qdisc_priv(root_qd); | ||
710 | struct Qdisc *qdisc = q->queues[queue]; | ||
711 | struct sk_buff *skb = NULL; | ||
712 | u32 len = qdisc->q.qlen; | ||
713 | |||
714 | if (!qdisc || !qdisc->dequeue) | ||
715 | return; | ||
716 | |||
717 | printk(KERN_DEBUG "requeue: qlen = %d\n", qdisc->q.qlen); | ||
718 | for (len = qdisc->q.qlen; len > 0; len--) { | ||
719 | skb = qdisc->dequeue(qdisc); | ||
720 | root_qd->q.qlen--; | ||
721 | /* packet will be classified again and */ | ||
722 | /* skb->packet_data->queue will be overridden if needed */ | ||
723 | if (skb) | ||
724 | wme_qdiscop_enqueue(skb, root_qd); | ||
725 | } | ||
726 | } | ||