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 | |
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')
-rw-r--r-- | net/mac80211/ieee80211.c | 8 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 5 | ||||
-rw-r--r-- | net/mac80211/tx.c | 2 | ||||
-rw-r--r-- | net/mac80211/wme.c | 135 | ||||
-rw-r--r-- | net/mac80211/wme.h | 23 |
5 files changed, 155 insertions, 18 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index c323c9af9b8f..f8e734f0da1a 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c | |||
@@ -462,7 +462,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
462 | spin_lock_bh(&local->mdev->queue_lock); | 462 | spin_lock_bh(&local->mdev->queue_lock); |
463 | 463 | ||
464 | /* create a new queue for this aggregation */ | 464 | /* create a new queue for this aggregation */ |
465 | /* ret = ieee80211_ht_agg_queue_add(local, sta, tid); */ | 465 | ret = ieee80211_ht_agg_queue_add(local, sta, tid); |
466 | 466 | ||
467 | /* case no queue is available to aggregation | 467 | /* case no queue is available to aggregation |
468 | * don't switch to aggregation */ | 468 | * don't switch to aggregation */ |
@@ -488,7 +488,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
488 | /* No need to requeue the packets in the agg queue, since we | 488 | /* No need to requeue the packets in the agg queue, since we |
489 | * held the tx lock: no packet could be enqueued to the newly | 489 | * held the tx lock: no packet could be enqueued to the newly |
490 | * allocated queue */ | 490 | * allocated queue */ |
491 | /* ieee80211_ht_agg_queue_remove(local, sta, tid, 0); */ | 491 | ieee80211_ht_agg_queue_remove(local, sta, tid, 0); |
492 | #ifdef CONFIG_MAC80211_HT_DEBUG | 492 | #ifdef CONFIG_MAC80211_HT_DEBUG |
493 | printk(KERN_DEBUG "BA request denied - HW or queue unavailable" | 493 | printk(KERN_DEBUG "BA request denied - HW or queue unavailable" |
494 | " for tid %d\n", tid); | 494 | " for tid %d\n", tid); |
@@ -499,7 +499,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
499 | } | 499 | } |
500 | 500 | ||
501 | /* Will put all the packets in the new SW queue */ | 501 | /* Will put all the packets in the new SW queue */ |
502 | /* ieee80211_requeue(local, ieee802_1d_to_ac[tid]); */ | 502 | ieee80211_requeue(local, ieee802_1d_to_ac[tid]); |
503 | spin_unlock_bh(&local->mdev->queue_lock); | 503 | spin_unlock_bh(&local->mdev->queue_lock); |
504 | 504 | ||
505 | /* We have most probably almost emptied the legacy queue */ | 505 | /* We have most probably almost emptied the legacy queue */ |
@@ -675,7 +675,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
675 | * the content of the qdiscs */ | 675 | * the content of the qdiscs */ |
676 | spin_lock_bh(&local->mdev->queue_lock); | 676 | spin_lock_bh(&local->mdev->queue_lock); |
677 | /* remove the queue for this aggregation */ | 677 | /* remove the queue for this aggregation */ |
678 | /* ieee80211_ht_agg_queue_remove(local, sta, tid, 1); */ | 678 | ieee80211_ht_agg_queue_remove(local, sta, tid, 1); |
679 | spin_unlock_bh(&local->mdev->queue_lock); | 679 | spin_unlock_bh(&local->mdev->queue_lock); |
680 | 680 | ||
681 | /* we just requeued the all the frames that were in the removed | 681 | /* we just requeued the all the frames that were in the removed |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a24c2c6ebc1..cfd0717c0033 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -165,6 +165,7 @@ struct ieee80211_txrx_data { | |||
165 | #define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1) | 165 | #define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1) |
166 | #define IEEE80211_TXPD_REQUEUE BIT(2) | 166 | #define IEEE80211_TXPD_REQUEUE BIT(2) |
167 | #define IEEE80211_TXPD_EAPOL_FRAME BIT(3) | 167 | #define IEEE80211_TXPD_EAPOL_FRAME BIT(3) |
168 | #define IEEE80211_TXPD_AMPDU BIT(4) | ||
168 | /* Stored in sk_buff->cb */ | 169 | /* Stored in sk_buff->cb */ |
169 | struct ieee80211_tx_packet_data { | 170 | struct ieee80211_tx_packet_data { |
170 | int ifindex; | 171 | int ifindex; |
@@ -452,8 +453,8 @@ struct ieee80211_local { | |||
452 | struct sta_info *sta_hash[STA_HASH_SIZE]; | 453 | struct sta_info *sta_hash[STA_HASH_SIZE]; |
453 | struct timer_list sta_cleanup; | 454 | struct timer_list sta_cleanup; |
454 | 455 | ||
455 | unsigned long state[NUM_TX_DATA_QUEUES]; | 456 | unsigned long state[NUM_TX_DATA_QUEUES_AMPDU]; |
456 | struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES]; | 457 | struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES_AMPDU]; |
457 | struct tasklet_struct tx_pending_tasklet; | 458 | struct tasklet_struct tx_pending_tasklet; |
458 | 459 | ||
459 | /* number of interfaces with corresponding IFF_ flags */ | 460 | /* number of interfaces with corresponding IFF_ flags */ |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 85d01646abf5..38e1b2bd8245 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1260,6 +1260,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, | |||
1260 | control.flags |= IEEE80211_TXCTL_REQUEUE; | 1260 | control.flags |= IEEE80211_TXCTL_REQUEUE; |
1261 | if (pkt_data->flags & IEEE80211_TXPD_EAPOL_FRAME) | 1261 | if (pkt_data->flags & IEEE80211_TXPD_EAPOL_FRAME) |
1262 | control.flags |= IEEE80211_TXCTL_EAPOL_FRAME; | 1262 | control.flags |= IEEE80211_TXCTL_EAPOL_FRAME; |
1263 | if (pkt_data->flags & IEEE80211_TXPD_AMPDU) | ||
1264 | control.flags |= IEEE80211_TXCTL_AMPDU; | ||
1263 | control.queue = pkt_data->queue; | 1265 | control.queue = pkt_data->queue; |
1264 | 1266 | ||
1265 | ret = ieee80211_tx(odev, skb, &control); | 1267 | ret = ieee80211_tx(odev, skb, &control); |
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 | } | ||
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 76c713a6450c..fcc6b05508cc 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | #define QOS_CONTROL_TAG1D_MASK 0x07 | 25 | #define QOS_CONTROL_TAG1D_MASK 0x07 |
26 | 26 | ||
27 | extern const int ieee802_1d_to_ac[8]; | ||
28 | |||
27 | static inline int WLAN_FC_IS_QOS_DATA(u16 fc) | 29 | static inline int WLAN_FC_IS_QOS_DATA(u16 fc) |
28 | { | 30 | { |
29 | return (fc & 0x8C) == 0x88; | 31 | return (fc & 0x8C) == 0x88; |
@@ -32,7 +34,12 @@ static inline int WLAN_FC_IS_QOS_DATA(u16 fc) | |||
32 | #ifdef CONFIG_NET_SCHED | 34 | #ifdef CONFIG_NET_SCHED |
33 | void ieee80211_install_qdisc(struct net_device *dev); | 35 | void ieee80211_install_qdisc(struct net_device *dev); |
34 | int ieee80211_qdisc_installed(struct net_device *dev); | 36 | int ieee80211_qdisc_installed(struct net_device *dev); |
35 | 37 | int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, | |
38 | struct sta_info *sta, u16 tid); | ||
39 | void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, | ||
40 | struct sta_info *sta, u16 tid, | ||
41 | u8 requeue); | ||
42 | void ieee80211_requeue(struct ieee80211_local *local, int queue); | ||
36 | int ieee80211_wme_register(void); | 43 | int ieee80211_wme_register(void); |
37 | void ieee80211_wme_unregister(void); | 44 | void ieee80211_wme_unregister(void); |
38 | #else | 45 | #else |
@@ -43,7 +50,19 @@ static inline int ieee80211_qdisc_installed(struct net_device *dev) | |||
43 | { | 50 | { |
44 | return 0; | 51 | return 0; |
45 | } | 52 | } |
46 | 53 | static inline int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, | |
54 | struct sta_info *sta, u16 tid) | ||
55 | { | ||
56 | return -EAGAIN; | ||
57 | } | ||
58 | static inline void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, | ||
59 | struct sta_info *sta, u16 tid, | ||
60 | u8 requeue) | ||
61 | { | ||
62 | } | ||
63 | static inline void ieee80211_requeue(struct ieee80211_local *local, int queue) | ||
64 | { | ||
65 | } | ||
47 | static inline int ieee80211_wme_register(void) | 66 | static inline int ieee80211_wme_register(void) |
48 | { | 67 | { |
49 | return 0; | 68 | return 0; |