diff options
author | Ron Rindjunsky <ron.rindjunsky@intel.com> | 2008-01-28 07:07:18 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:19:17 -0500 |
commit | 9e7234923789897858e1a475c579b5e2e6ad5b74 (patch) | |
tree | 69e97df9ff34f9237bfddee6e99100e8e16e1da1 /net/mac80211/wme.c | |
parent | eadc8d9e9047266a8914eb2ed4d36e797ce540d1 (diff) |
mac80211: A-MPDU Tx adding qdisc support
This patch allows qdisc support in A-MPDU Tx. a method to
handle QoS <-> TID switches is present in this patch.
Signed-off-by: Ron Rindjunsky <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/wme.c')
-rw-r--r-- | net/mac80211/wme.c | 135 |
1 files changed, 125 insertions, 10 deletions
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 4e236599dd31..425aa8588ea0 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; | ||
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,25 @@ 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 | sta = sta_info_get(local, hdr->addr1); | ||
157 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
158 | if (sta) { | ||
159 | int ampdu_queue = sta->tid_to_tx_q[tid]; | ||
160 | if ((ampdu_queue < local->hw.queues) && | ||
161 | test_bit(ampdu_queue, &q->qdisc_pool)) { | ||
162 | queue = ampdu_queue; | ||
163 | pkt_data->flags |= IEEE80211_TXPD_AMPDU; | ||
164 | } else { | ||
165 | pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; | ||
166 | } | ||
167 | sta_info_put(sta); | ||
168 | } | ||
169 | skb_queue_tail(&q->requeued[queue], skb); | ||
152 | qd->q.qlen++; | 170 | qd->q.qlen++; |
153 | return 0; | 171 | return 0; |
154 | } | 172 | } |
@@ -159,14 +177,28 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) | |||
159 | */ | 177 | */ |
160 | if (WLAN_FC_IS_QOS_DATA(fc)) { | 178 | if (WLAN_FC_IS_QOS_DATA(fc)) { |
161 | u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; | 179 | u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; |
162 | u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK; | 180 | u8 ack_policy = 0; |
181 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
163 | if (local->wifi_wme_noack_test) | 182 | if (local->wifi_wme_noack_test) |
164 | qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK << | 183 | ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << |
165 | QOS_CONTROL_ACK_POLICY_SHIFT; | 184 | QOS_CONTROL_ACK_POLICY_SHIFT; |
166 | /* qos header is 2 bytes, second reserved */ | 185 | /* qos header is 2 bytes, second reserved */ |
167 | *p = qos_hdr; | 186 | *p = ack_policy | tid; |
168 | p++; | 187 | p++; |
169 | *p = 0; | 188 | *p = 0; |
189 | |||
190 | sta = sta_info_get(local, hdr->addr1); | ||
191 | if (sta) { | ||
192 | int ampdu_queue = sta->tid_to_tx_q[tid]; | ||
193 | if ((ampdu_queue < local->hw.queues) && | ||
194 | test_bit(ampdu_queue, &q->qdisc_pool)) { | ||
195 | queue = ampdu_queue; | ||
196 | pkt_data->flags |= IEEE80211_TXPD_AMPDU; | ||
197 | } else { | ||
198 | pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; | ||
199 | } | ||
200 | sta_info_put(sta); | ||
201 | } | ||
170 | } | 202 | } |
171 | 203 | ||
172 | if (unlikely(queue >= local->hw.queues)) { | 204 | if (unlikely(queue >= local->hw.queues)) { |
@@ -184,6 +216,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) | |||
184 | kfree_skb(skb); | 216 | kfree_skb(skb); |
185 | err = NET_XMIT_DROP; | 217 | err = NET_XMIT_DROP; |
186 | } else { | 218 | } else { |
219 | tid = skb->priority & QOS_CONTROL_TAG1D_MASK; | ||
187 | pkt_data->queue = (unsigned int) queue; | 220 | pkt_data->queue = (unsigned int) queue; |
188 | qdisc = q->queues[queue]; | 221 | qdisc = q->queues[queue]; |
189 | err = qdisc->enqueue(skb, qdisc); | 222 | err = qdisc->enqueue(skb, qdisc); |
@@ -235,10 +268,11 @@ static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) | |||
235 | /* check all the h/w queues in numeric/priority order */ | 268 | /* check all the h/w queues in numeric/priority order */ |
236 | for (queue = 0; queue < hw->queues; queue++) { | 269 | for (queue = 0; queue < hw->queues; queue++) { |
237 | /* see if there is room in this hardware queue */ | 270 | /* see if there is room in this hardware queue */ |
238 | if (test_bit(IEEE80211_LINK_STATE_XOFF, | 271 | if ((test_bit(IEEE80211_LINK_STATE_XOFF, |
239 | &local->state[queue]) || | 272 | &local->state[queue])) || |
240 | test_bit(IEEE80211_LINK_STATE_PENDING, | 273 | (test_bit(IEEE80211_LINK_STATE_PENDING, |
241 | &local->state[queue])) | 274 | &local->state[queue])) || |
275 | (!test_bit(queue, &q->qdisc_pool))) | ||
242 | continue; | 276 | continue; |
243 | 277 | ||
244 | /* there is space - try and get a frame */ | 278 | /* there is space - try and get a frame */ |
@@ -360,6 +394,10 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) | |||
360 | } | 394 | } |
361 | } | 395 | } |
362 | 396 | ||
397 | /* reserve all legacy QoS queues */ | ||
398 | for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++) | ||
399 | set_bit(i, &q->qdisc_pool); | ||
400 | |||
363 | return err; | 401 | return err; |
364 | } | 402 | } |
365 | 403 | ||
@@ -605,3 +643,80 @@ void ieee80211_wme_unregister(void) | |||
605 | { | 643 | { |
606 | unregister_qdisc(&wme_qdisc_ops); | 644 | unregister_qdisc(&wme_qdisc_ops); |
607 | } | 645 | } |
646 | |||
647 | int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, | ||
648 | struct sta_info *sta, u16 tid) | ||
649 | { | ||
650 | int i; | ||
651 | struct ieee80211_sched_data *q = | ||
652 | qdisc_priv(local->mdev->qdisc_sleeping); | ||
653 | DECLARE_MAC_BUF(mac); | ||
654 | |||
655 | /* prepare the filter and save it for the SW queue | ||
656 | * matching the recieved HW queue */ | ||
657 | |||
658 | /* try to get a Qdisc from the pool */ | ||
659 | for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++) | ||
660 | if (!test_and_set_bit(i, &q->qdisc_pool)) { | ||
661 | ieee80211_stop_queue(local_to_hw(local), i); | ||
662 | sta->tid_to_tx_q[tid] = i; | ||
663 | |||
664 | /* IF there are already pending packets | ||
665 | * on this tid first we need to drain them | ||
666 | * on the previous queue | ||
667 | * since HT is strict in order */ | ||
668 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
669 | if (net_ratelimit()) | ||
670 | printk(KERN_DEBUG "allocated aggregation queue" | ||
671 | " %d tid %d addr %s pool=0x%lX\n", | ||
672 | i, tid, print_mac(mac, sta->addr), | ||
673 | q->qdisc_pool); | ||
674 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | return -EAGAIN; | ||
679 | } | ||
680 | |||
681 | /** | ||
682 | * the caller needs to hold local->mdev->queue_lock | ||
683 | */ | ||
684 | void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, | ||
685 | struct sta_info *sta, u16 tid, | ||
686 | u8 requeue) | ||
687 | { | ||
688 | struct ieee80211_sched_data *q = | ||
689 | qdisc_priv(local->mdev->qdisc_sleeping); | ||
690 | int agg_queue = sta->tid_to_tx_q[tid]; | ||
691 | |||
692 | /* return the qdisc to the pool */ | ||
693 | clear_bit(agg_queue, &q->qdisc_pool); | ||
694 | sta->tid_to_tx_q[tid] = local->hw.queues; | ||
695 | |||
696 | if (requeue) | ||
697 | ieee80211_requeue(local, agg_queue); | ||
698 | else | ||
699 | q->queues[agg_queue]->ops->reset(q->queues[agg_queue]); | ||
700 | } | ||
701 | |||
702 | void ieee80211_requeue(struct ieee80211_local *local, int queue) | ||
703 | { | ||
704 | struct Qdisc *root_qd = local->mdev->qdisc_sleeping; | ||
705 | struct ieee80211_sched_data *q = qdisc_priv(root_qd); | ||
706 | struct Qdisc *qdisc = q->queues[queue]; | ||
707 | struct sk_buff *skb = NULL; | ||
708 | u32 len = qdisc->q.qlen; | ||
709 | |||
710 | if (!qdisc || !qdisc->dequeue) | ||
711 | return; | ||
712 | |||
713 | printk(KERN_DEBUG "requeue: qlen = %d\n", qdisc->q.qlen); | ||
714 | for (len = qdisc->q.qlen; len > 0; len--) { | ||
715 | skb = qdisc->dequeue(qdisc); | ||
716 | root_qd->q.qlen--; | ||
717 | /* packet will be classified again and */ | ||
718 | /* skb->packet_data->queue will be overridden if needed */ | ||
719 | if (skb) | ||
720 | wme_qdiscop_enqueue(skb, root_qd); | ||
721 | } | ||
722 | } | ||