diff options
Diffstat (limited to 'net/mac80211/wme.c')
-rw-r--r-- | net/mac80211/wme.c | 166 |
1 files changed, 65 insertions, 101 deletions
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 5d09e8698b5..ffe1af82fa4 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -19,16 +19,22 @@ #include "wme.h" /* maximum number of hardware queues we support. */ -#define TC_80211_MAX_QUEUES 16 +#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES) +/* current number of hardware queues we support. */ +#define QD_NUM(hw) ((hw)->queues + (hw)->ampdu_queues) +/* + * Default mapping in classifier to work with default + * queue setup. + */ 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)]; + unsigned long qdisc_pool[BITS_TO_LONGS(QD_MAX_QUEUES)]; struct tcf_proto *filter_list; - struct Qdisc *queues[TC_80211_MAX_QUEUES]; - struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; + struct Qdisc *queues[QD_MAX_QUEUES]; + struct sk_buff_head requeued[QD_MAX_QUEUES]; }; static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; @@ -95,28 +101,22 @@ static inline int wme_downgrade_ac(struct sk_buff *skb) /* positive return value indicates which queue to use * negative return value indicates to drop the frame */ -static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) +static int classify80211(struct sk_buff *skb, struct Qdisc *qd) { struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - unsigned short fc = le16_to_cpu(hdr->frame_control); - int qos; - /* see if frame is data or non data frame */ - if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) { + if (!ieee80211_is_data(hdr->frame_control)) { /* management frames go on AC_VO queue, but are sent * without QoS control fields */ - return IEEE80211_TX_QUEUE_DATA0; + return 0; } if (0 /* injected */) { /* use AC from radiotap */ } - /* is this a QoS frame? */ - qos = fc & IEEE80211_STYPE_QOS_DATA; - - if (!qos) { + if (!ieee80211_is_data_qos(hdr->frame_control)) { skb->priority = 0; /* required for correct WPA/11i MIC */ return ieee802_1d_to_ac[skb->priority]; } @@ -141,29 +141,28 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) { struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; struct ieee80211_sched_data *q = qdisc_priv(qd); - struct ieee80211_tx_packet_data *pkt_data = - (struct ieee80211_tx_packet_data *) skb->cb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - unsigned short fc = le16_to_cpu(hdr->frame_control); struct Qdisc *qdisc; - int err, queue; struct sta_info *sta; + int err, queue; u8 tid; - if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) { - queue = pkt_data->queue; + if (info->flags & IEEE80211_TX_CTL_REQUEUE) { + queue = skb_get_queue_mapping(skb); 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) && + if ((ampdu_queue < QD_NUM(hw)) && test_bit(ampdu_queue, q->qdisc_pool)) { queue = ampdu_queue; - pkt_data->flags |= IEEE80211_TXPD_AMPDU; + info->flags |= IEEE80211_TX_CTL_AMPDU; } else { - pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; + info->flags &= ~IEEE80211_TX_CTL_AMPDU; } } rcu_read_unlock(); @@ -174,18 +173,20 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) queue = classify80211(skb, qd); + if (unlikely(queue >= local->hw.queues)) + queue = local->hw.queues - 1; + /* now we know the 1d priority, fill in the QoS header if there is one */ - if (WLAN_FC_IS_QOS_DATA(fc)) { - u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy = 0; tid = skb->priority & QOS_CONTROL_TAG1D_MASK; if (local->wifi_wme_noack_test) ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; /* qos header is 2 bytes, second reserved */ - *p = ack_policy | tid; - p++; + *p++ = ack_policy | tid; *p = 0; rcu_read_lock(); @@ -193,35 +194,24 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) 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)) { + if ((ampdu_queue < QD_NUM(hw)) && + test_bit(ampdu_queue, q->qdisc_pool)) { queue = ampdu_queue; - pkt_data->flags |= IEEE80211_TXPD_AMPDU; + info->flags |= IEEE80211_TX_CTL_AMPDU; } else { - pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; + info->flags &= ~IEEE80211_TX_CTL_AMPDU; } } rcu_read_unlock(); } - if (unlikely(queue >= local->hw.queues)) { -#if 0 - if (net_ratelimit()) { - printk(KERN_DEBUG "%s - queue=%d (hw does not " - "support) -> %d\n", - __func__, queue, local->hw.queues - 1); - } -#endif - queue = local->hw.queues - 1; - } - if (unlikely(queue < 0)) { kfree_skb(skb); err = NET_XMIT_DROP; } else { tid = skb->priority & QOS_CONTROL_TAG1D_MASK; - pkt_data->queue = (unsigned int) queue; + skb_set_queue_mapping(skb, queue); qdisc = q->queues[queue]; err = qdisc->enqueue(skb, qdisc); if (err == NET_XMIT_SUCCESS) { @@ -242,13 +232,11 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd) { struct ieee80211_sched_data *q = qdisc_priv(qd); - struct ieee80211_tx_packet_data *pkt_data = - (struct ieee80211_tx_packet_data *) skb->cb; struct Qdisc *qdisc; int err; /* we recorded which queue to use earlier! */ - qdisc = q->queues[pkt_data->queue]; + qdisc = q->queues[skb_get_queue_mapping(skb)]; if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) { qd->q.qlen++; @@ -270,13 +258,10 @@ static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) int queue; /* check all the h/w queues in numeric/priority order */ - for (queue = 0; queue < hw->queues; queue++) { + for (queue = 0; queue < QD_NUM(hw); 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])) || - (!test_bit(queue, q->qdisc_pool))) + if (__netif_subqueue_stopped(local->mdev, queue) || + !test_bit(queue, q->qdisc_pool)) continue; /* there is space - try and get a frame */ @@ -308,7 +293,7 @@ static void wme_qdiscop_reset(struct Qdisc* qd) /* QUESTION: should we have some hardware flush functionality here? */ - for (queue = 0; queue < hw->queues; queue++) { + for (queue = 0; queue < QD_NUM(hw); queue++) { skb_queue_purge(&q->requeued[queue]); qdisc_reset(q->queues[queue]); } @@ -325,7 +310,7 @@ static void wme_qdiscop_destroy(struct Qdisc* qd) tcf_destroy_chain(&q->filter_list); - for (queue=0; queue < hw->queues; queue++) { + for (queue = 0; queue < QD_NUM(hw); queue++) { skb_queue_purge(&q->requeued[queue]); qdisc_destroy(q->queues[queue]); q->queues[queue] = &noop_qdisc; @@ -336,17 +321,6 @@ 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 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 = nla_data(opt); - - if (opt->nla_len < nla_attr_size(sizeof(*qopt))) - return -EINVAL; - memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue)); -*/ return 0; } @@ -357,7 +331,7 @@ 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; struct ieee80211_local *local; - int queues; + struct ieee80211_hw *hw; int err = 0, i; /* check that device is a mac80211 device */ @@ -365,29 +339,26 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) return -EINVAL; - /* check this device is an ieee80211 master type device */ - if (dev->type != ARPHRD_IEEE80211) + local = wdev_priv(dev->ieee80211_ptr); + hw = &local->hw; + + /* only allow on master dev */ + if (dev != local->mdev) return -EINVAL; - /* check that there is no qdisc currently attached to device - * this ensures that we will be the root qdisc. (I can't find a better - * way to test this explicitly) */ - if (dev->qdisc_sleeping != &noop_qdisc) + /* ensure that we are root qdisc */ + if (qd->parent != TC_H_ROOT) return -EINVAL; if (qd->flags & TCQ_F_INGRESS) return -EINVAL; - local = wdev_priv(dev->ieee80211_ptr); - queues = local->hw.queues; - /* if options were passed in, set them */ - if (opt) { + if (opt) err = wme_qdiscop_tune(qd, opt); - } /* create child queues */ - for (i = 0; i < queues; i++) { + for (i = 0; i < QD_NUM(hw); i++) { skb_queue_head_init(&q->requeued[i]); q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops, qd->handle); @@ -398,8 +369,8 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) } } - /* reserve all legacy QoS queues */ - for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++) + /* non-aggregation queues: reserve/mark as used */ + for (i = 0; i < local->hw.queues; i++) set_bit(i, q->qdisc_pool); return err; @@ -407,16 +378,6 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb) { -/* struct ieee80211_sched_data *q = qdisc_priv(qd); - unsigned char *p = skb->tail; - struct tc_ieee80211_qopt opt; - - memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1); - NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); -*/ return skb->len; -/* -nla_put_failure: - skb_trim(skb, p - skb->data);*/ return -1; } @@ -429,7 +390,7 @@ static int wme_classop_graft(struct Qdisc *qd, unsigned long arg, struct ieee80211_hw *hw = &local->hw; unsigned long queue = arg - 1; - if (queue >= hw->queues) + if (queue >= QD_NUM(hw)) return -EINVAL; if (!new) @@ -453,7 +414,7 @@ wme_classop_leaf(struct Qdisc *qd, unsigned long arg) struct ieee80211_hw *hw = &local->hw; unsigned long queue = arg - 1; - if (queue >= hw->queues) + if (queue >= QD_NUM(hw)) return NULL; return q->queues[queue]; @@ -466,7 +427,7 @@ static unsigned long wme_classop_get(struct Qdisc *qd, u32 classid) struct ieee80211_hw *hw = &local->hw; unsigned long queue = TC_H_MIN(classid); - if (queue - 1 >= hw->queues) + if (queue - 1 >= QD_NUM(hw)) return 0; return queue; @@ -492,7 +453,7 @@ static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent, struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); struct ieee80211_hw *hw = &local->hw; - if (cl - 1 > hw->queues) + if (cl - 1 > QD_NUM(hw)) return -ENOENT; /* TODO: put code to program hardware queue parameters here, @@ -509,7 +470,7 @@ static int wme_classop_delete(struct Qdisc *qd, unsigned long cl) struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); struct ieee80211_hw *hw = &local->hw; - if (cl - 1 > hw->queues) + if (cl - 1 > QD_NUM(hw)) return -ENOENT; return 0; } @@ -522,7 +483,7 @@ static int wme_classop_dump_class(struct Qdisc *qd, unsigned long cl, struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); struct ieee80211_hw *hw = &local->hw; - if (cl - 1 > hw->queues) + if (cl - 1 > QD_NUM(hw)) return -ENOENT; tcm->tcm_handle = TC_H_MIN(cl); tcm->tcm_parent = qd->handle; @@ -540,7 +501,7 @@ static void wme_classop_walk(struct Qdisc *qd, struct qdisc_walker *arg) if (arg->stop) return; - for (queue = 0; queue < hw->queues; queue++) { + for (queue = 0; queue < QD_NUM(hw); queue++) { if (arg->count < arg->skip) { arg->count++; continue; @@ -657,10 +618,13 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, DECLARE_MAC_BUF(mac); /* prepare the filter and save it for the SW queue - * matching the recieved HW queue */ + * matching the received HW queue */ + + if (!local->hw.ampdu_queues) + return -EPERM; /* try to get a Qdisc from the pool */ - for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++) + for (i = local->hw.queues; i < QD_NUM(&local->hw); 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; @@ -689,13 +653,14 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid, u8 requeue) { + struct ieee80211_hw *hw = &local->hw; 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; + sta->tid_to_tx_q[tid] = QD_NUM(hw); if (requeue) ieee80211_requeue(local, agg_queue); @@ -714,7 +679,6 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) 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--; |