X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=net%2Fmac80211%2Fwme.c;h=64faa3dc488f70d2cfb09fae1bb1c3470424153e;hb=ac6aadb24b7d4f0e54246732e221c102073412bf;hp=7ab82b376e1bc92f8d656bfa30f7a3cf4b2b1152;hpb=6abd2c860e34add677de50e8b134f5af6f4b0893;p=linux-2.6-omap-h63xx.git diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 7ab82b376e1..64faa3dc488 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -18,80 +18,20 @@ #include "ieee80211_i.h" #include "wme.h" -static inline int WLAN_FC_IS_QOS_DATA(u16 fc) -{ - return (fc & 0x8C) == 0x88; -} - - -ieee80211_txrx_result -ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) -{ - u8 *data = rx->skb->data; - int tid; - - /* does the frame have a qos control field? */ - if (WLAN_FC_IS_QOS_DATA(rx->fc)) { - u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; - /* frame has qos control */ - tid = qc[0] & QOS_CONTROL_TID_MASK; - } else { - if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { - /* Separate TID for management frames */ - tid = NUM_RX_DATA_QUEUES - 1; - } else { - /* no qos control present */ - tid = 0; /* 802.1d - Best Effort */ - } - } -#ifdef CONFIG_MAC80211_DEBUG_COUNTERS - I802_DEBUG_INC(rx->local->wme_rx_queue[tid]); - if (rx->sta) { - I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]); - } -#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */ - - rx->u.rx.queue = tid; - /* Set skb->priority to 1d tag if highest order bit of TID is not set. - * For now, set skb->priority to 0 for other cases. */ - rx->skb->priority = (tid > 7) ? 0 : tid; - - return TXRX_CONTINUE; -} - - -ieee80211_txrx_result -ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx) -{ - u16 fc = rx->fc; - u8 *data = rx->skb->data; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data; - - if (!WLAN_FC_IS_QOS_DATA(fc)) - return TXRX_CONTINUE; - - /* remove the qos control field, update frame type and meta-data */ - memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2); - hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2); - /* change frame type to non QOS */ - rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA; - hdr->frame_control = cpu_to_le16(fc); - - return TXRX_CONTINUE; -} - - -#ifdef CONFIG_NET_SCHED /* maximum number of hardware queues we support. */ -#define TC_80211_MAX_QUEUES 8 +#define TC_80211_MAX_QUEUES 16 + +const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; struct ieee80211_sched_data { + unsigned long qdisc_pool[BITS_TO_LONGS(TC_80211_MAX_QUEUES)]; struct tcf_proto *filter_list; struct Qdisc *queues[TC_80211_MAX_QUEUES]; struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; }; +static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; /* given a data frame determine the 802.1p/1d tag to use */ static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) @@ -118,12 +58,12 @@ static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) return skb->priority - 256; /* check there is a valid IP header present */ - offset = ieee80211_get_hdrlen_from_skb(skb) + 8 /* LLC + proto */; - if (skb->protocol != __constant_htons(ETH_P_IP) || - skb->len < offset + sizeof(*ip)) + offset = ieee80211_get_hdrlen_from_skb(skb); + if (skb->len < offset + sizeof(llc_ip_hdr) + sizeof(*ip) || + memcmp(skb->data + offset, llc_ip_hdr, sizeof(llc_ip_hdr))) return 0; - ip = (struct iphdr *) (skb->data + offset); + ip = (struct iphdr *) (skb->data + offset + sizeof(llc_ip_hdr)); dscp = ip->tos & 0xfc; if (dscp & 0x1c) @@ -158,12 +98,9 @@ static inline int wme_downgrade_ac(struct sk_buff *skb) static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) { struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); - struct ieee80211_tx_packet_data *pkt_data = - (struct ieee80211_tx_packet_data *) skb->cb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; unsigned short fc = le16_to_cpu(hdr->frame_control); int qos; - const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; /* see if frame is data or non data frame */ if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) { @@ -172,12 +109,8 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) return IEEE80211_TX_QUEUE_DATA0; } - if (unlikely(pkt_data->mgmt_iface)) { - /* Data frames from hostapd (mainly, EAPOL) use AC_VO - * and they will include QoS control fields if - * the target STA is using WME. */ - skb->priority = 7; - return ieee802_1d_to_ac[skb->priority]; + if (0 /* injected */) { + /* use AC from radiotap */ } /* is this a QoS frame? */ @@ -189,14 +122,13 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) } /* use the data classifier to determine what 802.1d tag the - * data frame has */ + * data frame has */ skb->priority = classify_1d(skb, qd); - /* incase we are a client verify acm is not set for this ac */ + /* in case we are a client verify acm is not set for this ac */ while (unlikely(local->wmm_acm & BIT(skb->priority))) { if (wme_downgrade_ac(skb)) { - /* No AC with lower priority has acm=0, - * drop packet. */ + /* No AC with lower priority has acm=0, drop packet. */ return -1; } } @@ -216,9 +148,26 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) unsigned short fc = le16_to_cpu(hdr->frame_control); struct Qdisc *qdisc; int err, queue; - - if (pkt_data->requeue) { - skb_queue_tail(&q->requeued[pkt_data->queue], skb); + struct sta_info *sta; + u8 tid; + + if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) { + queue = pkt_data->queue; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); + tid = skb->priority & QOS_CONTROL_TAG1D_MASK; + if (sta) { + int ampdu_queue = sta->tid_to_tx_q[tid]; + if ((ampdu_queue < local->hw.queues) && + test_bit(ampdu_queue, q->qdisc_pool)) { + queue = ampdu_queue; + pkt_data->flags |= IEEE80211_TXPD_AMPDU; + } else { + pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; + } + } + rcu_read_unlock(); + skb_queue_tail(&q->requeued[queue], skb); qd->q.qlen++; return 0; } @@ -229,14 +178,31 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) */ if (WLAN_FC_IS_QOS_DATA(fc)) { u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; - u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK; + u8 ack_policy = 0; + tid = skb->priority & QOS_CONTROL_TAG1D_MASK; if (local->wifi_wme_noack_test) - qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK << + ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; /* qos header is 2 bytes, second reserved */ - *p = qos_hdr; + *p = ack_policy | tid; p++; *p = 0; + + rcu_read_lock(); + + sta = sta_info_get(local, hdr->addr1); + if (sta) { + int ampdu_queue = sta->tid_to_tx_q[tid]; + if ((ampdu_queue < local->hw.queues) && + test_bit(ampdu_queue, q->qdisc_pool)) { + queue = ampdu_queue; + pkt_data->flags |= IEEE80211_TXPD_AMPDU; + } else { + pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; + } + } + + rcu_read_unlock(); } if (unlikely(queue >= local->hw.queues)) { @@ -254,6 +220,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) kfree_skb(skb); err = NET_XMIT_DROP; } else { + tid = skb->priority & QOS_CONTROL_TAG1D_MASK; pkt_data->queue = (unsigned int) queue; qdisc = q->queues[queue]; err = qdisc->enqueue(skb, qdisc); @@ -305,10 +272,11 @@ static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) /* check all the h/w queues in numeric/priority order */ for (queue = 0; queue < hw->queues; queue++) { /* see if there is room in this hardware queue */ - if (test_bit(IEEE80211_LINK_STATE_XOFF, - &local->state[queue]) || - test_bit(IEEE80211_LINK_STATE_PENDING, - &local->state[queue])) + if ((test_bit(IEEE80211_LINK_STATE_XOFF, + &local->state[queue])) || + (test_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[queue])) || + (!test_bit(queue, q->qdisc_pool))) continue; /* there is space - try and get a frame */ @@ -367,16 +335,16 @@ static void wme_qdiscop_destroy(struct Qdisc* qd) /* called whenever parameters are updated on existing qdisc */ -static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) +static int wme_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt) { /* struct ieee80211_sched_data *q = qdisc_priv(qd); */ /* check our options block is the right size */ /* copy any options to our local structure */ /* Ignore options block for now - always use static mapping - struct tc_ieee80211_qopt *qopt = RTA_DATA(opt); + struct tc_ieee80211_qopt *qopt = nla_data(opt); - if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) + if (opt->nla_len < nla_attr_size(sizeof(*qopt))) return -EINVAL; memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue)); */ @@ -385,7 +353,7 @@ static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) /* called during initial creation of qdisc on device */ -static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) +static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) { struct ieee80211_sched_data *q = qdisc_priv(qd); struct net_device *dev = qd->dev; @@ -430,6 +398,10 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) } } + /* reserve all legacy QoS queues */ + for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++) + set_bit(i, q->qdisc_pool); + return err; } @@ -440,10 +412,10 @@ static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb) struct tc_ieee80211_qopt opt; memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1); - RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); + NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); */ return skb->len; /* -rtattr_failure: +nla_put_failure: skb_trim(skb, p - skb->data);*/ return -1; } @@ -514,7 +486,7 @@ static void wme_classop_put(struct Qdisc *q, unsigned long cl) static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent, - struct rtattr **tca, unsigned long *arg) + struct nlattr **tca, unsigned long *arg) { unsigned long cl = *arg; struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); @@ -598,7 +570,7 @@ static struct tcf_proto ** wme_classop_find_tcf(struct Qdisc *qd, /* this qdisc is classful (i.e. has classes, some of which may have leaf qdiscs attached) * - these are the operations on the classes */ -static struct Qdisc_class_ops class_ops = +static const struct Qdisc_class_ops class_ops = { .graft = wme_classop_graft, .leaf = wme_classop_leaf, @@ -618,7 +590,7 @@ static struct Qdisc_class_ops class_ops = /* queueing discipline operations */ -static struct Qdisc_ops wme_qdisc_ops = +static struct Qdisc_ops wme_qdisc_ops __read_mostly = { .next = NULL, .cl_ops = &class_ops, @@ -675,4 +647,80 @@ void ieee80211_wme_unregister(void) { unregister_qdisc(&wme_qdisc_ops); } -#endif /* CONFIG_NET_SCHED */ + +int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + int i; + struct ieee80211_sched_data *q = + qdisc_priv(local->mdev->qdisc_sleeping); + DECLARE_MAC_BUF(mac); + + /* prepare the filter and save it for the SW queue + * matching the recieved HW queue */ + + /* try to get a Qdisc from the pool */ + for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++) + if (!test_and_set_bit(i, q->qdisc_pool)) { + ieee80211_stop_queue(local_to_hw(local), i); + sta->tid_to_tx_q[tid] = i; + + /* IF there are already pending packets + * on this tid first we need to drain them + * on the previous queue + * since HT is strict in order */ +#ifdef CONFIG_MAC80211_HT_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "allocated aggregation queue" + " %d tid %d addr %s pool=0x%lX", + i, tid, print_mac(mac, sta->addr), + q->qdisc_pool[0]); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + return 0; + } + + return -EAGAIN; +} + +/** + * the caller needs to hold local->mdev->queue_lock + */ +void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, + struct sta_info *sta, u16 tid, + u8 requeue) +{ + struct ieee80211_sched_data *q = + qdisc_priv(local->mdev->qdisc_sleeping); + int agg_queue = sta->tid_to_tx_q[tid]; + + /* return the qdisc to the pool */ + clear_bit(agg_queue, q->qdisc_pool); + sta->tid_to_tx_q[tid] = local->hw.queues; + + if (requeue) + ieee80211_requeue(local, agg_queue); + else + q->queues[agg_queue]->ops->reset(q->queues[agg_queue]); +} + +void ieee80211_requeue(struct ieee80211_local *local, int queue) +{ + struct Qdisc *root_qd = local->mdev->qdisc_sleeping; + struct ieee80211_sched_data *q = qdisc_priv(root_qd); + struct Qdisc *qdisc = q->queues[queue]; + struct sk_buff *skb = NULL; + u32 len; + + if (!qdisc || !qdisc->dequeue) + return; + + printk(KERN_DEBUG "requeue: qlen = %d\n", qdisc->q.qlen); + for (len = qdisc->q.qlen; len > 0; len--) { + skb = qdisc->dequeue(qdisc); + root_qd->q.qlen--; + /* packet will be classified again and */ + /* skb->packet_data->queue will be overridden if needed */ + if (skb) + wme_qdiscop_enqueue(skb, root_qd); + } +}