2007-05-05 11:45:53 -07:00
/*
* Copyright 2004 , Instant802 Networks , Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/module.h>
# include <linux/if_arp.h>
# include <linux/types.h>
# include <net/ip.h>
# include <net/pkt_sched.h>
# include <net/mac80211.h>
# include "ieee80211_i.h"
# include "wme.h"
2008-07-15 03:34:57 -07:00
/* Default mapping in classifier to work with default
2008-04-30 18:51:21 +02:00
* queue setup .
*/
2008-01-28 14:07:18 +02:00
const int ieee802_1d_to_ac [ 8 ] = { 2 , 3 , 3 , 2 , 1 , 1 , 0 , 0 } ;
2007-05-05 11:45:53 -07:00
2008-01-09 19:12:48 +02:00
static const char llc_ip_hdr [ 8 ] = { 0xAA , 0xAA , 0x3 , 0 , 0 , 0 , 0x08 , 0 } ;
2007-05-05 11:45:53 -07:00
2008-07-15 03:34:57 -07:00
/* Given a data frame determine the 802.1p/1d tag to use. */
static unsigned int classify_1d ( struct sk_buff * skb )
2007-05-05 11:45:53 -07:00
{
2008-07-15 03:34:57 -07:00
unsigned int dscp ;
2007-05-05 11:45:53 -07:00
/* skb->priority values from 256->263 are magic values to
2008-07-15 03:34:57 -07:00
* directly indicate a specific 802.1 d priority . This is used
* to allow 802.1 d priority to be passed directly in from VLAN
* tags , etc .
*/
2007-05-05 11:45:53 -07:00
if ( skb - > priority > = 256 & & skb - > priority < = 263 )
return skb - > priority - 256 ;
2008-07-15 03:34:57 -07:00
switch ( skb - > protocol ) {
2008-09-20 22:20:49 -07:00
case htons ( ETH_P_IP ) :
2008-07-15 03:34:57 -07:00
dscp = ip_hdr ( skb ) - > tos & 0xfc ;
break ;
2007-05-05 11:45:53 -07:00
2008-07-15 03:34:57 -07:00
default :
return 0 ;
}
2007-05-05 11:45:53 -07:00
return dscp > > 5 ;
}
2008-07-15 03:34:57 -07:00
static int wme_downgrade_ac ( struct sk_buff * skb )
2007-05-05 11:45:53 -07:00
{
switch ( skb - > priority ) {
case 6 :
case 7 :
skb - > priority = 5 ; /* VO -> VI */
return 0 ;
case 4 :
case 5 :
skb - > priority = 3 ; /* VI -> BE */
return 0 ;
case 0 :
case 3 :
skb - > priority = 2 ; /* BE -> BK */
return 0 ;
default :
return - 1 ;
}
}
2008-07-15 03:34:57 -07:00
/* Indicate which queue to use. */
2008-09-26 13:34:54 +02:00
static u16 classify80211 ( struct ieee80211_local * local , struct sk_buff * skb )
2007-05-05 11:45:53 -07:00
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2008-06-11 14:21:59 -07:00
if ( ! ieee80211_is_data ( hdr - > frame_control ) ) {
2007-05-05 11:45:53 -07:00
/* management frames go on AC_VO queue, but are sent
* without QoS control fields */
2008-04-30 18:51:21 +02:00
return 0 ;
2007-05-05 11:45:53 -07:00
}
2007-09-28 14:02:09 +02:00
if ( 0 /* injected */ ) {
/* use AC from radiotap */
2007-05-05 11:45:53 -07:00
}
2008-06-11 14:21:59 -07:00
if ( ! ieee80211_is_data_qos ( hdr - > frame_control ) ) {
2007-05-05 11:45:53 -07:00
skb - > priority = 0 ; /* required for correct WPA/11i MIC */
return ieee802_1d_to_ac [ skb - > priority ] ;
}
/* use the data classifier to determine what 802.1d tag the
2007-08-28 17:01:55 -04:00
* data frame has */
2008-07-15 03:34:57 -07:00
skb - > priority = classify_1d ( skb ) ;
2007-05-05 11:45:53 -07:00
2007-08-28 17:01:55 -04:00
/* in case we are a client verify acm is not set for this ac */
2007-05-05 11:45:53 -07:00
while ( unlikely ( local - > wmm_acm & BIT ( skb - > priority ) ) ) {
if ( wme_downgrade_ac ( skb ) ) {
2008-07-15 03:34:57 -07:00
/* The old code would drop the packet in this
* case .
*/
return 0 ;
2007-05-05 11:45:53 -07:00
}
}
/* look up which queue to use for frames with this 1d tag */
return ieee802_1d_to_ac [ skb - > priority ] ;
}
2008-07-15 03:34:57 -07:00
u16 ieee80211_select_queue ( struct net_device * dev , struct sk_buff * skb )
2007-05-05 11:45:53 -07:00
{
2008-09-26 13:34:54 +02:00
struct ieee80211_master_priv * mpriv = netdev_priv ( dev ) ;
struct ieee80211_local * local = mpriv - > local ;
2008-10-24 09:55:27 +05:30
struct ieee80211_hw * hw = & local - > hw ;
2007-05-05 11:45:53 -07:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2008-01-28 14:07:18 +02:00
struct sta_info * sta ;
2008-07-15 03:34:57 -07:00
u16 queue ;
2008-01-28 14:07:18 +02:00
u8 tid ;
2007-05-05 11:45:53 -07:00
2008-09-26 13:34:54 +02:00
queue = classify80211 ( local , skb ) ;
2008-07-15 03:34:57 -07:00
if ( unlikely ( queue > = local - > hw . queues ) )
queue = local - > hw . queues - 1 ;
2008-10-24 09:55:27 +05:30
if ( skb - > requeue ) {
if ( ! hw - > ampdu_queues )
return queue ;
2008-02-25 16:27:46 +01:00
rcu_read_lock ( ) ;
2008-01-28 14:07:18 +02:00
sta = sta_info_get ( local , hdr - > addr1 ) ;
2008-07-02 11:05:34 -07:00
tid = skb - > priority & IEEE80211_QOS_CTL_TAG1D_MASK ;
2008-01-28 14:07:18 +02:00
if ( sta ) {
int ampdu_queue = sta - > tid_to_tx_q [ tid ] ;
2008-07-15 03:34:57 -07:00
if ( ( ampdu_queue < ieee80211_num_queues ( hw ) ) & &
2008-10-24 09:55:27 +05:30
test_bit ( ampdu_queue , local - > queue_pool ) )
2008-01-28 14:07:18 +02:00
queue = ampdu_queue ;
}
2008-02-25 16:27:46 +01:00
rcu_read_unlock ( ) ;
2007-05-05 11:45:53 -07:00
2008-07-15 03:34:57 -07:00
return queue ;
}
2008-04-30 18:51:21 +02:00
2008-07-15 03:34:57 -07:00
/* Now we know the 1d priority, fill in the QoS header if
* there is one .
2007-05-05 11:45:53 -07:00
*/
2008-06-11 14:21:59 -07:00
if ( ieee80211_is_data_qos ( hdr - > frame_control ) ) {
u8 * p = ieee80211_get_qos_ctl ( hdr ) ;
2008-01-28 14:07:18 +02:00
u8 ack_policy = 0 ;
2008-07-02 11:05:34 -07:00
tid = skb - > priority & IEEE80211_QOS_CTL_TAG1D_MASK ;
2007-05-05 11:45:53 -07:00
if ( local - > wifi_wme_noack_test )
2008-01-28 14:07:18 +02:00
ack_policy | = QOS_CONTROL_ACK_POLICY_NOACK < <
2007-05-05 11:45:53 -07:00
QOS_CONTROL_ACK_POLICY_SHIFT ;
/* qos header is 2 bytes, second reserved */
2008-06-11 14:21:59 -07:00
* p + + = ack_policy | tid ;
2007-05-05 11:45:53 -07:00
* p = 0 ;
2008-01-28 14:07:18 +02:00
2008-10-24 09:55:27 +05:30
if ( ! hw - > ampdu_queues )
return queue ;
2008-02-25 16:27:46 +01:00
rcu_read_lock ( ) ;
2008-01-28 14:07:18 +02:00
sta = sta_info_get ( local , hdr - > addr1 ) ;
if ( sta ) {
int ampdu_queue = sta - > tid_to_tx_q [ tid ] ;
2008-07-15 03:34:57 -07:00
if ( ( ampdu_queue < ieee80211_num_queues ( hw ) ) & &
2008-10-24 09:55:27 +05:30
test_bit ( ampdu_queue , local - > queue_pool ) )
2008-01-28 14:07:18 +02:00
queue = ampdu_queue ;
}
2008-02-25 16:27:46 +01:00
rcu_read_unlock ( ) ;
2007-05-05 11:45:53 -07:00
}
return queue ;
}
2008-01-28 14:07:18 +02:00
int ieee80211_ht_agg_queue_add ( struct ieee80211_local * local ,
2008-07-15 03:34:57 -07:00
struct sta_info * sta , u16 tid )
2008-01-28 14:07:18 +02:00
{
int i ;
2008-07-29 11:32:07 +02:00
/* XXX: currently broken due to cb/requeue use */
return - EPERM ;
2008-01-28 14:07:18 +02:00
/* prepare the filter and save it for the SW queue
2008-04-30 18:51:21 +02:00
* matching the received HW queue */
if ( ! local - > hw . ampdu_queues )
return - EPERM ;
2008-01-28 14:07:18 +02:00
/* try to get a Qdisc from the pool */
2008-07-15 03:34:57 -07:00
for ( i = local - > hw . queues ; i < ieee80211_num_queues ( & local - > hw ) ; i + + )
if ( ! test_and_set_bit ( i , local - > queue_pool ) ) {
2008-01-28 14:07:18 +02:00
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
2008-10-27 15:56:10 -07:00
if ( net_ratelimit ( ) )
2008-01-28 14:07:18 +02:00
printk ( KERN_DEBUG " allocated aggregation queue "
2008-10-27 15:56:10 -07:00
" %d tid %d addr %pM pool=0x%lX \n " ,
i , tid , sta - > sta . addr ,
2008-07-15 03:34:57 -07:00
local - > queue_pool [ 0 ] ) ;
2008-01-28 14:07:18 +02:00
# endif /* CONFIG_MAC80211_HT_DEBUG */
return 0 ;
}
return - EAGAIN ;
}
/**
2008-07-17 00:34:19 -07:00
* the caller needs to hold netdev_get_tx_queue ( local - > mdev , X ) - > lock
2008-01-28 14:07:18 +02:00
*/
void ieee80211_ht_agg_queue_remove ( struct ieee80211_local * local ,
struct sta_info * sta , u16 tid ,
u8 requeue )
{
int agg_queue = sta - > tid_to_tx_q [ tid ] ;
2008-07-15 03:34:57 -07:00
struct ieee80211_hw * hw = & local - > hw ;
2008-01-28 14:07:18 +02:00
/* return the qdisc to the pool */
2008-07-15 03:34:57 -07:00
clear_bit ( agg_queue , local - > queue_pool ) ;
sta - > tid_to_tx_q [ tid ] = ieee80211_num_queues ( hw ) ;
2008-01-28 14:07:18 +02:00
2008-07-15 03:34:57 -07:00
if ( requeue ) {
2008-01-28 14:07:18 +02:00
ieee80211_requeue ( local , agg_queue ) ;
2008-07-15 03:34:57 -07:00
} else {
struct netdev_queue * txq ;
2008-07-17 00:53:03 -07:00
spinlock_t * root_lock ;
2008-08-02 23:25:50 -07:00
struct Qdisc * q ;
2008-07-15 03:34:57 -07:00
txq = netdev_get_tx_queue ( local - > mdev , agg_queue ) ;
2008-08-02 23:25:50 -07:00
q = rcu_dereference ( txq - > qdisc ) ;
root_lock = qdisc_lock ( q ) ;
2008-07-15 03:34:57 -07:00
2008-07-17 00:53:03 -07:00
spin_lock_bh ( root_lock ) ;
2008-08-02 23:25:50 -07:00
qdisc_reset ( q ) ;
2008-07-17 00:53:03 -07:00
spin_unlock_bh ( root_lock ) ;
2008-07-15 03:34:57 -07:00
}
2008-01-28 14:07:18 +02:00
}
void ieee80211_requeue ( struct ieee80211_local * local , int queue )
{
2008-07-15 03:34:57 -07:00
struct netdev_queue * txq = netdev_get_tx_queue ( local - > mdev , queue ) ;
struct sk_buff_head list ;
2008-07-17 00:53:03 -07:00
spinlock_t * root_lock ;
2008-07-15 03:34:57 -07:00
struct Qdisc * qdisc ;
2008-04-23 13:45:12 +03:00
u32 len ;
2008-01-28 14:07:18 +02:00
2008-07-15 03:34:57 -07:00
rcu_read_lock_bh ( ) ;
qdisc = rcu_dereference ( txq - > qdisc ) ;
2008-01-28 14:07:18 +02:00
if ( ! qdisc | | ! qdisc - > dequeue )
2008-07-15 03:34:57 -07:00
goto out_unlock ;
skb_queue_head_init ( & list ) ;
2008-01-28 14:07:18 +02:00
2008-07-17 00:53:03 -07:00
root_lock = qdisc_root_lock ( qdisc ) ;
spin_lock ( root_lock ) ;
2008-01-28 14:07:18 +02:00
for ( len = qdisc - > q . qlen ; len > 0 ; len - - ) {
2008-07-15 03:34:57 -07:00
struct sk_buff * skb = qdisc - > dequeue ( qdisc ) ;
2008-01-28 14:07:18 +02:00
if ( skb )
2008-07-15 03:34:57 -07:00
__skb_queue_tail ( & list , skb ) ;
}
2008-07-17 00:53:03 -07:00
spin_unlock ( root_lock ) ;
2008-07-15 03:34:57 -07:00
for ( len = list . qlen ; len > 0 ; len - - ) {
struct sk_buff * skb = __skb_dequeue ( & list ) ;
u16 new_queue ;
BUG_ON ( ! skb ) ;
new_queue = ieee80211_select_queue ( local - > mdev , skb ) ;
skb_set_queue_mapping ( skb , new_queue ) ;
txq = netdev_get_tx_queue ( local - > mdev , new_queue ) ;
qdisc = rcu_dereference ( txq - > qdisc ) ;
2008-07-17 00:53:03 -07:00
root_lock = qdisc_root_lock ( qdisc ) ;
2008-07-15 03:34:57 -07:00
2008-07-17 00:53:03 -07:00
spin_lock ( root_lock ) ;
2008-07-20 00:08:04 -07:00
qdisc_enqueue_root ( skb , qdisc ) ;
2008-07-17 00:53:03 -07:00
spin_unlock ( root_lock ) ;
2008-01-28 14:07:18 +02:00
}
2008-07-15 03:34:57 -07:00
out_unlock :
rcu_read_unlock_bh ( ) ;
2008-01-28 14:07:18 +02:00
}