summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/802/fddi.c4
-rw-r--r--net/802/hippi.c5
-rw-r--r--net/8021q/vlan_dev.c1
-rw-r--r--net/appletalk/dev.c11
-rw-r--r--net/atm/br2684.c26
-rw-r--r--net/atm/clip.c27
-rw-r--r--net/bridge/br_fdb.c2
-rw-r--r--net/core/dev.c133
-rw-r--r--net/core/gen_estimator.c4
-rw-r--r--net/core/net-sysfs.c1
-rw-r--r--net/core/netpoll.c5
-rw-r--r--net/core/pktgen.c3
-rw-r--r--net/core/skbuff.c70
-rw-r--r--net/core/sock.c103
-rw-r--r--net/decnet/af_decnet.c19
-rw-r--r--net/decnet/dn_nsp_out.c8
-rw-r--r--net/econet/af_econet.c18
-rw-r--r--net/ethernet/eth.c5
-rw-r--r--net/ipv4/Kconfig2
-rw-r--r--net/ipv4/af_inet.c21
-rw-r--r--net/ipv4/fib_trie.c6
-rw-r--r--net/ipv4/route.c60
-rw-r--r--net/ipv4/tcp.c47
-rw-r--r--net/ipv4/tcp_input.c118
-rw-r--r--net/ipv6/af_inet6.c13
-rw-r--r--net/ipv6/route.c3
-rw-r--r--net/ipv6/tcp_ipv6.c7
-rw-r--r--net/irda/irlap_frame.c18
-rw-r--r--net/llc/llc_conn.c4
-rw-r--r--net/mac80211/debugfs.c16
-rw-r--r--net/mac80211/ibss.c33
-rw-r--r--net/mac80211/ieee80211_i.h43
-rw-r--r--net/mac80211/key.c6
-rw-r--r--net/mac80211/main.c24
-rw-r--r--net/mac80211/mesh.c40
-rw-r--r--net/mac80211/mesh.h16
-rw-r--r--net/mac80211/mesh_hwmp.c8
-rw-r--r--net/mac80211/mesh_plink.c21
-rw-r--r--net/mac80211/mlme.c227
-rw-r--r--net/mac80211/pm.c80
-rw-r--r--net/mac80211/rx.c94
-rw-r--r--net/mac80211/scan.c18
-rw-r--r--net/mac80211/spectmgmt.c101
-rw-r--r--net/mac80211/sta_info.c6
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/tx.c2
-rw-r--r--net/mac80211/util.c116
-rw-r--r--net/mac80211/wext.c5
-rw-r--r--net/mac80211/wme.c30
-rw-r--r--net/rxrpc/ar-connection.c12
-rw-r--r--net/sched/sch_teql.c1
-rw-r--r--net/wireless/Kconfig8
-rw-r--r--net/wireless/Makefile1
-rw-r--r--net/wireless/core.c7
-rw-r--r--net/wireless/core.h14
-rw-r--r--net/wireless/debugfs.c131
-rw-r--r--net/wireless/debugfs.h14
-rw-r--r--net/wireless/nl80211.c68
-rw-r--r--net/wireless/reg.c223
-rw-r--r--net/wireless/util.c320
-rw-r--r--net/wireless/wext-compat.c7
-rw-r--r--net/wireless/wext.c39
62 files changed, 1607 insertions, 870 deletions
diff --git a/net/802/fddi.c b/net/802/fddi.c
index 539e6064e6d4..3ef0ab0a543a 100644
--- a/net/802/fddi.c
+++ b/net/802/fddi.c
@@ -185,10 +185,6 @@ static const struct header_ops fddi_header_ops = {
static void fddi_setup(struct net_device *dev)
{
dev->header_ops = &fddi_header_ops;
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- dev->change_mtu = fddi_change_mtu,
-#endif
-
dev->type = ARPHRD_FDDI;
dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */
dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */
diff --git a/net/802/hippi.c b/net/802/hippi.c
index 313b9ebf92ee..cd3e8e929529 100644
--- a/net/802/hippi.c
+++ b/net/802/hippi.c
@@ -193,11 +193,6 @@ static const struct header_ops hippi_header_ops = {
static void hippi_setup(struct net_device *dev)
{
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- dev->change_mtu = hippi_change_mtu;
- dev->set_mac_address = hippi_mac_addr;
- dev->neigh_setup = hippi_neigh_setup_dev;
-#endif
dev->header_ops = &hippi_header_ops;
/*
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index ff7572ac5481..1e2ad4c7c59b 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -644,7 +644,6 @@ static int vlan_dev_init(struct net_device *dev)
dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
dev->netdev_ops = &vlan_netdev_ops;
}
- netdev_resync_ops(dev);
if (is_vlan_dev(real_dev))
subclass = 1;
diff --git a/net/appletalk/dev.c b/net/appletalk/dev.c
index 72277d70c980..6c8016f61866 100644
--- a/net/appletalk/dev.c
+++ b/net/appletalk/dev.c
@@ -9,21 +9,10 @@
#include <linux/if_arp.h>
#include <linux/if_ltalk.h>
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
-static int ltalk_change_mtu(struct net_device *dev, int mtu)
-{
- return -EINVAL;
-}
-#endif
-
static void ltalk_setup(struct net_device *dev)
{
/* Fill in the fields of the device structure with localtalk-generic values. */
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- dev->change_mtu = ltalk_change_mtu;
-#endif
-
dev->type = ARPHRD_LOCALTLK;
dev->hard_header_len = LTALK_HLEN;
dev->mtu = LTALK_MTU;
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 3100a8940afc..bfa8fa9894fc 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -445,9 +445,10 @@ free_skb:
*/
static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
{
+ struct sk_buff_head queue;
int err;
struct br2684_vcc *brvcc;
- struct sk_buff *skb;
+ struct sk_buff *skb, *tmp;
struct sk_buff_head *rq;
struct br2684_dev *brdev;
struct net_device *net_dev;
@@ -505,29 +506,20 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
barrier();
atmvcc->push = br2684_push;
+ __skb_queue_head_init(&queue);
rq = &sk_atm(atmvcc)->sk_receive_queue;
spin_lock_irqsave(&rq->lock, flags);
- if (skb_queue_empty(rq)) {
- skb = NULL;
- } else {
- /* NULL terminate the list. */
- rq->prev->next = NULL;
- skb = rq->next;
- }
- rq->prev = rq->next = (struct sk_buff *)rq;
- rq->qlen = 0;
+ skb_queue_splice_init(rq, &queue);
spin_unlock_irqrestore(&rq->lock, flags);
- while (skb) {
- struct sk_buff *next = skb->next;
+ skb_queue_walk_safe(&queue, skb, tmp) {
+ struct net_device *dev = skb->dev;
- skb->next = skb->prev = NULL;
- br2684_push(atmvcc, skb);
- skb->dev->stats.rx_bytes -= skb->len;
- skb->dev->stats.rx_packets--;
+ dev->stats.rx_bytes -= skb->len;
+ dev->stats.rx_packets--;
- skb = next;
+ br2684_push(atmvcc, skb);
}
__module_get(THIS_MODULE);
return 0;
diff --git a/net/atm/clip.c b/net/atm/clip.c
index 3dc0a3a42a57..5597b87b9e64 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -445,9 +445,9 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
static int clip_mkip(struct atm_vcc *vcc, int timeout)
{
+ struct sk_buff_head *rq, queue;
struct clip_vcc *clip_vcc;
- struct sk_buff *skb;
- struct sk_buff_head *rq;
+ struct sk_buff *skb, *tmp;
unsigned long flags;
if (!vcc->push)
@@ -469,39 +469,28 @@ static int clip_mkip(struct atm_vcc *vcc, int timeout)
vcc->push = clip_push;
vcc->pop = clip_pop;
+ __skb_queue_head_init(&queue);
rq = &sk_atm(vcc)->sk_receive_queue;
spin_lock_irqsave(&rq->lock, flags);
- if (skb_queue_empty(rq)) {
- skb = NULL;
- } else {
- /* NULL terminate the list. */
- rq->prev->next = NULL;
- skb = rq->next;
- }
- rq->prev = rq->next = (struct sk_buff *)rq;
- rq->qlen = 0;
+ skb_queue_splice_init(rq, &queue);
spin_unlock_irqrestore(&rq->lock, flags);
/* re-process everything received between connection setup and MKIP */
- while (skb) {
- struct sk_buff *next = skb->next;
-
- skb->next = skb->prev = NULL;
+ skb_queue_walk_safe(&queue, skb, tmp) {
if (!clip_devs) {
atm_return(vcc, skb->truesize);
kfree_skb(skb);
} else {
+ struct net_device *dev = skb->dev;
unsigned int len = skb->len;
skb_get(skb);
clip_push(vcc, skb);
- skb->dev->stats.rx_packets--;
- skb->dev->stats.rx_bytes -= len;
+ dev->stats.rx_packets--;
+ dev->stats.rx_bytes -= len;
kfree_skb(skb);
}
-
- skb = next;
}
return 0;
}
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index a48f5efdb6bf..cb3e97b93aeb 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -398,7 +398,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
if (unlikely(fdb->is_local)) {
if (net_ratelimit())
printk(KERN_WARNING "%s: received packet with "
- " own address as source address\n",
+ "own address as source address\n",
source->dev->name);
} else {
/* fastpath: update of existing entry */
diff --git a/net/core/dev.c b/net/core/dev.c
index 3942266d1f6c..32ceee17896e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1698,6 +1698,8 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
skb->dst = NULL;
}
rc = ops->ndo_start_xmit(skb, dev);
+ if (rc == 0)
+ txq_trans_update(txq);
/*
* TODO: if skb_orphan() was called by
* dev->hard_start_xmit() (for example, the unmodified
@@ -1727,6 +1729,7 @@ gso:
skb->next = nskb;
return rc;
}
+ txq_trans_update(txq);
if (unlikely(netif_tx_queue_stopped(txq) && skb->next))
return NETDEV_TX_BUSY;
} while (skb->next);
@@ -2387,21 +2390,6 @@ void napi_gro_flush(struct napi_struct *napi)
}
EXPORT_SYMBOL(napi_gro_flush);
-void *skb_gro_header(struct sk_buff *skb, unsigned int hlen)
-{
- unsigned int offset = skb_gro_offset(skb);
-
- hlen += offset;
- if (unlikely(skb_headlen(skb) ||
- skb_shinfo(skb)->frags[0].size < hlen ||
- PageHighMem(skb_shinfo(skb)->frags[0].page)))
- return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL;
-
- return page_address(skb_shinfo(skb)->frags[0].page) +
- skb_shinfo(skb)->frags[0].page_offset + offset;
-}
-EXPORT_SYMBOL(skb_gro_header);
-
int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff **pp = NULL;
@@ -2464,10 +2452,25 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
ret = GRO_HELD;
pull:
- if (unlikely(!pskb_may_pull(skb, skb_gro_offset(skb)))) {
- if (napi->gro_list == skb)
- napi->gro_list = skb->next;
- ret = GRO_DROP;
+ if (skb_headlen(skb) < skb_gro_offset(skb)) {
+ int grow = skb_gro_offset(skb) - skb_headlen(skb);
+
+ BUG_ON(skb->end - skb->tail < grow);
+
+ memcpy(skb_tail_pointer(skb), NAPI_GRO_CB(skb)->frag0, grow);
+
+ skb->tail += grow;
+ skb->data_len -= grow;
+
+ skb_shinfo(skb)->frags[0].page_offset += grow;
+ skb_shinfo(skb)->frags[0].size -= grow;
+
+ if (unlikely(!skb_shinfo(skb)->frags[0].size)) {
+ put_page(skb_shinfo(skb)->frags[0].page);
+ memmove(skb_shinfo(skb)->frags,
+ skb_shinfo(skb)->frags + 1,
+ --skb_shinfo(skb)->nr_frags);
+ }
}
ok:
@@ -2517,6 +2520,22 @@ int napi_skb_finish(int ret, struct sk_buff *skb)
}
EXPORT_SYMBOL(napi_skb_finish);
+void skb_gro_reset_offset(struct sk_buff *skb)
+{
+ NAPI_GRO_CB(skb)->data_offset = 0;
+ NAPI_GRO_CB(skb)->frag0 = NULL;
+ NAPI_GRO_CB(skb)->frag0_len = 0;
+
+ if (skb->mac_header == skb->tail &&
+ !PageHighMem(skb_shinfo(skb)->frags[0].page)) {
+ NAPI_GRO_CB(skb)->frag0 =
+ page_address(skb_shinfo(skb)->frags[0].page) +
+ skb_shinfo(skb)->frags[0].page_offset;
+ NAPI_GRO_CB(skb)->frag0_len = skb_shinfo(skb)->frags[0].size;
+ }
+}
+EXPORT_SYMBOL(skb_gro_reset_offset);
+
int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
skb_gro_reset_offset(skb);
@@ -2586,17 +2605,24 @@ struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
struct sk_buff *skb = napi->skb;
struct ethhdr *eth;
+ unsigned int hlen;
+ unsigned int off;
napi->skb = NULL;
skb_reset_mac_header(skb);
skb_gro_reset_offset(skb);
- eth = skb_gro_header(skb, sizeof(*eth));
- if (!eth) {
- napi_reuse_skb(napi, skb);
- skb = NULL;
- goto out;
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*eth);
+ eth = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ eth = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!eth)) {
+ napi_reuse_skb(napi, skb);
+ skb = NULL;
+ goto out;
+ }
}
skb_gro_pull(skb, sizeof(*eth));
@@ -4580,39 +4606,6 @@ unsigned long netdev_fix_features(unsigned long features, const char *name)
}
EXPORT_SYMBOL(netdev_fix_features);
-/* Some devices need to (re-)set their netdev_ops inside
- * ->init() or similar. If that happens, we have to setup
- * the compat pointers again.
- */
-void netdev_resync_ops(struct net_device *dev)
-{
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- const struct net_device_ops *ops = dev->netdev_ops;
-
- dev->init = ops->ndo_init;
- dev->uninit = ops->ndo_uninit;
- dev->open = ops->ndo_open;
- dev->change_rx_flags = ops->ndo_change_rx_flags;
- dev->set_rx_mode = ops->ndo_set_rx_mode;
- dev->set_multicast_list = ops->ndo_set_multicast_list;
- dev->set_mac_address = ops->ndo_set_mac_address;
- dev->validate_addr = ops->ndo_validate_addr;
- dev->do_ioctl = ops->ndo_do_ioctl;
- dev->set_config = ops->ndo_set_config;
- dev->change_mtu = ops->ndo_change_mtu;
- dev->neigh_setup = ops->ndo_neigh_setup;
- dev->tx_timeout = ops->ndo_tx_timeout;
- dev->get_stats = ops->ndo_get_stats;
- dev->vlan_rx_register = ops->ndo_vlan_rx_register;
- dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
- dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ops->ndo_poll_controller;
-#endif
-#endif
-}
-EXPORT_SYMBOL(netdev_resync_ops);
-
/**
* register_netdevice - register a network device
* @dev: device to register
@@ -4652,23 +4645,6 @@ int register_netdevice(struct net_device *dev)
dev->iflink = -1;
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- /* Netdevice_ops API compatibility support.
- * This is temporary until all network devices are converted.
- */
- if (dev->netdev_ops) {
- netdev_resync_ops(dev);
- } else {
- char drivername[64];
- pr_info("%s (%s): not using net_device_ops yet\n",
- dev->name, netdev_drivername(dev, drivername, 64));
-
- /* This works only because net_device_ops and the
- compatibility structure are the same. */
- dev->netdev_ops = (void *) &(dev->init);
- }
-#endif
-
/* Init, if this function is available */
if (dev->netdev_ops->ndo_init) {
ret = dev->netdev_ops->ndo_init(dev);
@@ -5012,18 +4988,18 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
struct netdev_queue *tx;
struct net_device *dev;
size_t alloc_size;
- void *p;
+ struct net_device *p;
BUG_ON(strlen(name) >= sizeof(dev->name));
alloc_size = sizeof(struct net_device);
if (sizeof_priv) {
/* ensure 32-byte alignment of private area */
- alloc_size = (alloc_size + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
+ alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
alloc_size += sizeof_priv;
}
/* ensure 32-byte alignment of whole construct */
- alloc_size += NETDEV_ALIGN_CONST;
+ alloc_size += NETDEV_ALIGN - 1;
p = kzalloc(alloc_size, GFP_KERNEL);
if (!p) {
@@ -5038,8 +5014,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
goto free_p;
}
- dev = (struct net_device *)
- (((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
+ dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p;
if (dev_addr_init(dev))
diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c
index 6d62d4618cfc..78e5bfc454ae 100644
--- a/net/core/gen_estimator.c
+++ b/net/core/gen_estimator.c
@@ -128,12 +128,12 @@ static void est_timer(unsigned long arg)
npackets = e->bstats->packets;
brate = (nbytes - e->last_bytes)<<(7 - idx);
e->last_bytes = nbytes;
- e->avbps += ((s64)(brate - e->avbps)) >> e->ewma_log;
+ e->avbps += (brate >> e->ewma_log) - (e->avbps >> e->ewma_log);
e->rate_est->bps = (e->avbps+0xF)>>5;
rate = (npackets - e->last_packets)<<(12 - idx);
e->last_packets = npackets;
- e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log;
+ e->avpps += (rate >> e->ewma_log) - (e->avpps >> e->ewma_log);
e->rate_est->pps = (e->avpps+0x1FF)>>10;
skip:
read_unlock(&est_lock);
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index b9641e816eee..3994680c08b9 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -499,7 +499,6 @@ int netdev_register_kobject(struct net_device *net)
dev->platform_data = net;
dev->groups = groups;
- BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ);
dev_set_name(dev, "%s", net->name);
#ifdef CONFIG_SYSFS
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 67b4f3e3d4a5..7ab31a7576a1 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -302,8 +302,11 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
for (tries = jiffies_to_usecs(1)/USEC_PER_POLL;
tries > 0; --tries) {
if (__netif_tx_trylock(txq)) {
- if (!netif_tx_queue_stopped(txq))
+ if (!netif_tx_queue_stopped(txq)) {
status = ops->ndo_start_xmit(skb, dev);
+ if (status == NETDEV_TX_OK)
+ txq_trans_update(txq);
+ }
__netif_tx_unlock(txq);
if (status == NETDEV_TX_OK)
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 3779c1438c11..b8ccd3c88d63 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2447,7 +2447,7 @@ static inline void free_SAs(struct pktgen_dev *pkt_dev)
if (pkt_dev->cflows) {
/* let go of the SAs if we have them */
int i = 0;
- for (; i < pkt_dev->nflows; i++){
+ for (; i < pkt_dev->cflows; i++) {
struct xfrm_state *x = pkt_dev->flows[i].x;
if (x) {
xfrm_state_put(x);
@@ -3438,6 +3438,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
retry_now:
ret = (*xmit)(pkt_dev->skb, odev);
if (likely(ret == NETDEV_TX_OK)) {
+ txq_trans_update(txq);
pkt_dev->last_ok = 1;
pkt_dev->sofar++;
pkt_dev->seq_num++;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d152394b2611..8e815e685f28 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -526,8 +526,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->sp = secpath_get(old->sp);
#endif
memcpy(new->cb, old->cb, sizeof(old->cb));
- new->csum_start = old->csum_start;
- new->csum_offset = old->csum_offset;
+ new->csum = old->csum;
new->local_df = old->local_df;
new->pkt_type = old->pkt_type;
new->ip_summed = old->ip_summed;
@@ -538,6 +537,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
#endif
new->protocol = old->protocol;
new->mark = old->mark;
+ new->iif = old->iif;
__nf_copy(new, old);
#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
@@ -550,10 +550,18 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
#endif
#endif
new->vlan_tci = old->vlan_tci;
+#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
+ new->do_not_encrypt = old->do_not_encrypt;
+ new->requeue = old->requeue;
+#endif
skb_copy_secmark(new, old);
}
+/*
+ * You should not add any new code to this function. Add it to
+ * __copy_skb_header above instead.
+ */
static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
{
#define C(x) n->x = skb->x
@@ -569,16 +577,11 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
n->cloned = 1;
n->nohdr = 0;
n->destructor = NULL;
- C(iif);
C(tail);
C(end);
C(head);
C(data);
C(truesize);
-#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
- C(do_not_encrypt);
- C(requeue);
-#endif
atomic_set(&n->users, 1);
atomic_inc(&(skb_shinfo(skb)->dataref));
@@ -2288,7 +2291,7 @@ unsigned int skb_seq_read(unsigned int consumed, const u8 **data,
next_skb:
block_limit = skb_headlen(st->cur_skb) + st->stepped_offset;
- if (abs_offset < block_limit) {
+ if (abs_offset < block_limit && !st->frag_data) {
*data = st->cur_skb->data + (abs_offset - st->stepped_offset);
return block_limit - abs_offset;
}
@@ -2661,30 +2664,40 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
{
struct sk_buff *p = *head;
struct sk_buff *nskb;
+ struct skb_shared_info *skbinfo = skb_shinfo(skb);
+ struct skb_shared_info *pinfo = skb_shinfo(p);
unsigned int headroom;
unsigned int len = skb_gro_len(skb);
+ unsigned int offset = skb_gro_offset(skb);
+ unsigned int headlen = skb_headlen(skb);
if (p->len + len >= 65536)
return -E2BIG;
- if (skb_shinfo(p)->frag_list)
+ if (pinfo->frag_list)
goto merge;
- else if (skb_headlen(skb) <= skb_gro_offset(skb)) {
- if (skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags >
- MAX_SKB_FRAGS)
+ else if (headlen <= offset) {
+ skb_frag_t *frag;
+ skb_frag_t *frag2;
+ int i = skbinfo->nr_frags;
+ int nr_frags = pinfo->nr_frags + i;
+
+ offset -= headlen;
+
+ if (nr_frags > MAX_SKB_FRAGS)
return -E2BIG;
- skb_shinfo(skb)->frags[0].page_offset +=
- skb_gro_offset(skb) - skb_headlen(skb);
- skb_shinfo(skb)->frags[0].size -=
- skb_gro_offset(skb) - skb_headlen(skb);
+ pinfo->nr_frags = nr_frags;
+ skbinfo->nr_frags = 0;
- memcpy(skb_shinfo(p)->frags + skb_shinfo(p)->nr_frags,
- skb_shinfo(skb)->frags,
- skb_shinfo(skb)->nr_frags * sizeof(skb_frag_t));
+ frag = pinfo->frags + nr_frags;
+ frag2 = skbinfo->frags + i;
+ do {
+ *--frag = *--frag2;
+ } while (--i);
- skb_shinfo(p)->nr_frags += skb_shinfo(skb)->nr_frags;
- skb_shinfo(skb)->nr_frags = 0;
+ frag->page_offset += offset;
+ frag->size -= offset;
skb->truesize -= skb->data_len;
skb->len -= skb->data_len;
@@ -2715,7 +2728,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
*NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p);
skb_shinfo(nskb)->frag_list = p;
- skb_shinfo(nskb)->gso_size = skb_shinfo(p)->gso_size;
+ skb_shinfo(nskb)->gso_size = pinfo->gso_size;
skb_header_release(p);
nskb->prev = p;
@@ -2730,16 +2743,13 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
p = nskb;
merge:
- if (skb_gro_offset(skb) > skb_headlen(skb)) {
- skb_shinfo(skb)->frags[0].page_offset +=
- skb_gro_offset(skb) - skb_headlen(skb);
- skb_shinfo(skb)->frags[0].size -=
- skb_gro_offset(skb) - skb_headlen(skb);
- skb_gro_reset_offset(skb);
- skb_gro_pull(skb, skb_headlen(skb));
+ if (offset > headlen) {
+ skbinfo->frags[0].page_offset += offset - headlen;
+ skbinfo->frags[0].size -= offset - headlen;
+ offset = headlen;
}
- __skb_pull(skb, skb_gro_offset(skb));
+ __skb_pull(skb, offset);
p->prev->next = skb;
p->prev = skb;
diff --git a/net/core/sock.c b/net/core/sock.c
index 7dbf3ffb35cc..58dec9dff99a 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -212,6 +212,7 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX;
/* Maximal space eaten by iovec or ancilliary data plus some space */
int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
+EXPORT_SYMBOL(sysctl_optmem_max);
static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
{
@@ -444,7 +445,7 @@ static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
int sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
- struct sock *sk=sock->sk;
+ struct sock *sk = sock->sk;
int val;
int valbool;
struct linger ling;
@@ -463,15 +464,15 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
if (get_user(val, (int __user *)optval))
return -EFAULT;
- valbool = val?1:0;
+ valbool = val ? 1 : 0;
lock_sock(sk);
- switch(optname) {
+ switch (optname) {
case SO_DEBUG:
- if (val && !capable(CAP_NET_ADMIN)) {
+ if (val && !capable(CAP_NET_ADMIN))
ret = -EACCES;
- } else
+ else
sock_valbool_flag(sk, SOCK_DBG, valbool);
break;
case SO_REUSEADDR:
@@ -582,7 +583,7 @@ set_rcvbuf:
ret = -EINVAL; /* 1003.1g */
break;
}
- if (copy_from_user(&ling,optval,sizeof(ling))) {
+ if (copy_from_user(&ling, optval, sizeof(ling))) {
ret = -EFAULT;
break;
}
@@ -690,9 +691,8 @@ set_rcvbuf:
case SO_MARK:
if (!capable(CAP_NET_ADMIN))
ret = -EPERM;
- else {
+ else
sk->sk_mark = val;
- }
break;
/* We implement the SO_SNDLOWAT etc to
@@ -704,6 +704,7 @@ set_rcvbuf:
release_sock(sk);
return ret;
}
+EXPORT_SYMBOL(sock_setsockopt);
int sock_getsockopt(struct socket *sock, int level, int optname,
@@ -727,7 +728,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
memset(&v, 0, sizeof(v));
- switch(optname) {
+ switch (optname) {
case SO_DEBUG:
v.val = sock_flag(sk, SOCK_DBG);
break;
@@ -762,7 +763,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
case SO_ERROR:
v.val = -sock_error(sk);
- if (v.val==0)
+ if (v.val == 0)
v.val = xchg(&sk->sk_err_soft, 0);
break;
@@ -816,7 +817,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_RCVTIMEO:
- lv=sizeof(struct timeval);
+ lv = sizeof(struct timeval);
if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
v.tm.tv_sec = 0;
v.tm.tv_usec = 0;
@@ -827,7 +828,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_SNDTIMEO:
- lv=sizeof(struct timeval);
+ lv = sizeof(struct timeval);
if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) {
v.tm.tv_sec = 0;
v.tm.tv_usec = 0;
@@ -842,7 +843,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_SNDLOWAT:
- v.val=1;
+ v.val = 1;
break;
case SO_PASSCRED:
@@ -1002,6 +1003,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
return sk;
}
+EXPORT_SYMBOL(sk_alloc);
void sk_free(struct sock *sk)
{
@@ -1026,6 +1028,7 @@ void sk_free(struct sock *sk)
put_net(sock_net(sk));
sk_prot_free(sk->sk_prot_creator, sk);
}
+EXPORT_SYMBOL(sk_free);
/*
* Last sock_put should drop referrence to sk->sk_net. It has already
@@ -1126,7 +1129,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
out:
return newsk;
}
-
EXPORT_SYMBOL_GPL(sk_clone);
void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
@@ -1177,6 +1179,7 @@ void sock_wfree(struct sk_buff *skb)
sk->sk_write_space(sk);
sock_put(sk);
}
+EXPORT_SYMBOL(sock_wfree);
/*
* Read buffer destructor automatically called from kfree_skb.
@@ -1188,6 +1191,7 @@ void sock_rfree(struct sk_buff *skb)
atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
sk_mem_uncharge(skb->sk, skb->truesize);
}
+EXPORT_SYMBOL(sock_rfree);
int sock_i_uid(struct sock *sk)
@@ -1199,6 +1203,7 @@ int sock_i_uid(struct sock *sk)
read_unlock(&sk->sk_callback_lock);
return uid;
}
+EXPORT_SYMBOL(sock_i_uid);
unsigned long sock_i_ino(struct sock *sk)
{
@@ -1209,6 +1214,7 @@ unsigned long sock_i_ino(struct sock *sk)
read_unlock(&sk->sk_callback_lock);
return ino;
}
+EXPORT_SYMBOL(sock_i_ino);
/*
* Allocate a skb from the socket's send buffer.
@@ -1217,7 +1223,7 @@ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force,
gfp_t priority)
{
if (force || atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
- struct sk_buff * skb = alloc_skb(size, priority);
+ struct sk_buff *skb = alloc_skb(size, priority);
if (skb) {
skb_set_owner_w(skb, sk);
return skb;
@@ -1225,6 +1231,7 @@ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force,
}
return NULL;
}
+EXPORT_SYMBOL(sock_wmalloc);
/*
* Allocate a skb from the socket's receive buffer.
@@ -1261,6 +1268,7 @@ void *sock_kmalloc(struct sock *sk, int size, gfp_t priority)
}
return NULL;
}
+EXPORT_SYMBOL(sock_kmalloc);
/*
* Free an option memory block.
@@ -1270,11 +1278,12 @@ void sock_kfree_s(struct sock *sk, void *mem, int size)
kfree(mem);
atomic_sub(size, &sk->sk_omem_alloc);
}
+EXPORT_SYMBOL(sock_kfree_s);
/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
I think, these locks should be removed for datagram sockets.
*/
-static long sock_wait_for_wmem(struct sock * sk, long timeo)
+static long sock_wait_for_wmem(struct sock *sk, long timeo)
{
DEFINE_WAIT(wait);
@@ -1392,6 +1401,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
{
return sock_alloc_send_pskb(sk, size, 0, noblock, errcode);
}
+EXPORT_SYMBOL(sock_alloc_send_skb);
static void __lock_sock(struct sock *sk)
{
@@ -1460,7 +1470,6 @@ int sk_wait_data(struct sock *sk, long *timeo)
finish_wait(sk->sk_sleep, &wait);
return rc;
}
-
EXPORT_SYMBOL(sk_wait_data);
/**
@@ -1541,7 +1550,6 @@ suppress_allocation:
atomic_sub(amt, prot->memory_allocated);
return 0;
}
-
EXPORT_SYMBOL(__sk_mem_schedule);
/**
@@ -1560,7 +1568,6 @@ void __sk_mem_reclaim(struct sock *sk)
(atomic_read(prot->memory_allocated) < prot->sysctl_mem[0]))
*prot->memory_pressure = 0;
}
-
EXPORT_SYMBOL(__sk_mem_reclaim);
@@ -1575,78 +1582,92 @@ int sock_no_bind(struct socket *sock, struct sockaddr *saddr, int len)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_bind);
int sock_no_connect(struct socket *sock, struct sockaddr *saddr,
int len, int flags)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_connect);
int sock_no_socketpair(struct socket *sock1, struct socket *sock2)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_socketpair);
int sock_no_accept(struct socket *sock, struct socket *newsock, int flags)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_accept);
int sock_no_getname(struct socket *sock, struct sockaddr *saddr,
int *len, int peer)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_getname);
-unsigned int sock_no_poll(struct file * file, struct socket *sock, poll_table *pt)
+unsigned int sock_no_poll(struct file *file, struct socket *sock, poll_table *pt)
{
return 0;
}
+EXPORT_SYMBOL(sock_no_poll);
int sock_no_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_ioctl);
int sock_no_listen(struct socket *sock, int backlog)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_listen);
int sock_no_shutdown(struct socket *sock, int how)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_shutdown);
int sock_no_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_setsockopt);
int sock_no_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_getsockopt);
int sock_no_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
size_t len)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_sendmsg);
int sock_no_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
size_t len, int flags)
{
return -EOPNOTSUPP;
}
+EXPORT_SYMBOL(sock_no_recvmsg);
int sock_no_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma)
{
/* Mirror missing mmap method error code */
return -ENODEV;
}
+EXPORT_SYMBOL(sock_no_mmap);
ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
{
@@ -1660,6 +1681,7 @@ ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, siz
kunmap(page);
return res;
}
+EXPORT_SYMBOL(sock_no_sendpage);
/*
* Default Socket Callbacks
@@ -1723,6 +1745,7 @@ void sk_send_sigurg(struct sock *sk)
if (send_sigurg(&sk->sk_socket->file->f_owner))
sk_wake_async(sk, SOCK_WAKE_URG, POLL_PRI);
}
+EXPORT_SYMBOL(sk_send_sigurg);
void sk_reset_timer(struct sock *sk, struct timer_list* timer,
unsigned long expires)
@@ -1730,7 +1753,6 @@ void sk_reset_timer(struct sock *sk, struct timer_list* timer,
if (!mod_timer(timer, expires))
sock_hold(sk);
}
-
EXPORT_SYMBOL(sk_reset_timer);
void sk_stop_timer(struct sock *sk, struct timer_list* timer)
@@ -1738,7 +1760,6 @@ void sk_stop_timer(struct sock *sk, struct timer_list* timer)
if (timer_pending(timer) && del_timer(timer))
__sock_put(sk);
}
-
EXPORT_SYMBOL(sk_stop_timer);
void sock_init_data(struct socket *sock, struct sock *sk)
@@ -1797,6 +1818,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
atomic_set(&sk->sk_refcnt, 1);
atomic_set(&sk->sk_drops, 0);
}
+EXPORT_SYMBOL(sock_init_data);
void lock_sock_nested(struct sock *sk, int subclass)
{
@@ -1812,7 +1834,6 @@ void lock_sock_nested(struct sock *sk, int subclass)
mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
local_bh_enable();
}
-
EXPORT_SYMBOL(lock_sock_nested);
void release_sock(struct sock *sk)
@@ -1895,7 +1916,6 @@ int sock_common_getsockopt(struct socket *sock, int level, int optname,
return sk->sk_prot->getsockopt(sk, level, optname, optval, optlen);
}
-
EXPORT_SYMBOL(sock_common_getsockopt);
#ifdef CONFIG_COMPAT
@@ -1925,7 +1945,6 @@ int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_namelen = addr_len;
return err;
}
-
EXPORT_SYMBOL(sock_common_recvmsg);
/*
@@ -1938,7 +1957,6 @@ int sock_common_setsockopt(struct socket *sock, int level, int optname,
return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen);
}
-
EXPORT_SYMBOL(sock_common_setsockopt);
#ifdef CONFIG_COMPAT
@@ -1989,7 +2007,6 @@ void sk_common_release(struct sock *sk)
sk_refcnt_debug_release(sk);
sock_put(sk);
}
-
EXPORT_SYMBOL(sk_common_release);
static DEFINE_RWLOCK(proto_list_lock);
@@ -2171,7 +2188,6 @@ out_free_sock_slab:
out:
return -ENOBUFS;
}
-
EXPORT_SYMBOL(proto_register);
void proto_unregister(struct proto *prot)
@@ -2198,7 +2214,6 @@ void proto_unregister(struct proto *prot)
prot->twsk_prot->twsk_slab = NULL;
}
}
-
EXPORT_SYMBOL(proto_unregister);
#ifdef CONFIG_PROC_FS
@@ -2324,33 +2339,3 @@ static int __init proto_init(void)
subsys_initcall(proto_init);
#endif /* PROC_FS */
-
-EXPORT_SYMBOL(sk_alloc);
-EXPORT_SYMBOL(sk_free);
-EXPORT_SYMBOL(sk_send_sigurg);
-EXPORT_SYMBOL(sock_alloc_send_skb);
-EXPORT_SYMBOL(sock_init_data);
-EXPORT_SYMBOL(sock_kfree_s);
-EXPORT_SYMBOL(sock_kmalloc);
-EXPORT_SYMBOL(sock_no_accept);
-EXPORT_SYMBOL(sock_no_bind);
-EXPORT_SYMBOL(sock_no_connect);
-EXPORT_SYMBOL(sock_no_getname);
-EXPORT_SYMBOL(sock_no_getsockopt);
-EXPORT_SYMBOL(sock_no_ioctl);
-EXPORT_SYMBOL(sock_no_listen);
-EXPORT_SYMBOL(sock_no_mmap);
-EXPORT_SYMBOL(sock_no_poll);
-EXPORT_SYMBOL(sock_no_recvmsg);
-EXPORT_SYMBOL(sock_no_sendmsg);
-EXPORT_SYMBOL(sock_no_sendpage);
-EXPORT_SYMBOL(sock_no_setsockopt);
-EXPORT_SYMBOL(sock_no_shutdown);
-EXPORT_SYMBOL(sock_no_socketpair);
-EXPORT_SYMBOL(sock_rfree);
-EXPORT_SYMBOL(sock_setsockopt);
-EXPORT_SYMBOL(sock_wfree);
-EXPORT_SYMBOL(sock_wmalloc);
-EXPORT_SYMBOL(sock_i_uid);
-EXPORT_SYMBOL(sock_i_ino);
-EXPORT_SYMBOL(sysctl_optmem_max);
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 9647d911f916..bccb3887773e 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -1250,14 +1250,8 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if (skb) {
amount = skb->len;
} else {
- skb = sk->sk_receive_queue.next;
- for (;;) {
- if (skb ==
- (struct sk_buff *)&sk->sk_receive_queue)
- break;
+ skb_queue_walk(&sk->sk_receive_queue, skb)
amount += skb->len;
- skb = skb->next;
- }
}
release_sock(sk);
err = put_user(amount, (int __user *)arg);
@@ -1644,13 +1638,13 @@ static int __dn_getsockopt(struct socket *sock, int level,int optname, char __us
static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
{
- struct sk_buff *skb = q->next;
+ struct sk_buff *skb;
int len = 0;
if (flags & MSG_OOB)
return !skb_queue_empty(q) ? 1 : 0;
- while(skb != (struct sk_buff *)q) {
+ skb_queue_walk(q, skb) {
struct dn_skb_cb *cb = DN_SKB_CB(skb);
len += skb->len;
@@ -1666,8 +1660,6 @@ static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int
/* minimum data length for read exceeded */
if (len >= target)
return 1;
-
- skb = skb->next;
}
return 0;
@@ -1683,7 +1675,7 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock,
size_t target = size > 1 ? 1 : 0;
size_t copied = 0;
int rv = 0;
- struct sk_buff *skb, *nskb;
+ struct sk_buff *skb, *n;
struct dn_skb_cb *cb = NULL;
unsigned char eor = 0;
long timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
@@ -1758,7 +1750,7 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock,
finish_wait(sk->sk_sleep, &wait);
}
- for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
+ skb_queue_walk_safe(queue, skb, n) {
unsigned int chunk = skb->len;
cb = DN_SKB_CB(skb);
@@ -1775,7 +1767,6 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock,
skb_pull(skb, chunk);
eor = cb->nsp_flags & 0x40;
- nskb = skb->next;
if (skb->len == 0) {
skb_unlink(skb, queue);
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
index 2013c25b7f5a..da04f459337e 100644
--- a/net/decnet/dn_nsp_out.c
+++ b/net/decnet/dn_nsp_out.c
@@ -382,7 +382,7 @@ int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff
{
struct dn_skb_cb *cb = DN_SKB_CB(skb);
struct dn_scp *scp = DN_SK(sk);
- struct sk_buff *skb2, *list, *ack = NULL;
+ struct sk_buff *skb2, *n, *ack = NULL;
int wakeup = 0;
int try_retrans = 0;
unsigned long reftime = cb->stamp;
@@ -390,9 +390,7 @@ int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff
unsigned short xmit_count;
unsigned short segnum;
- skb2 = q->next;
- list = (struct sk_buff *)q;
- while(list != skb2) {
+ skb_queue_walk_safe(q, skb2, n) {
struct dn_skb_cb *cb2 = DN_SKB_CB(skb2);
if (dn_before_or_equal(cb2->segnum, acknum))
@@ -400,8 +398,6 @@ int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff
/* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */
- skb2 = skb2->next;
-
if (ack == NULL)
continue;
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index 6f479fa522c3..8121bf0029e3 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -901,15 +901,10 @@ static void aun_tx_ack(unsigned long seq, int result)
struct ec_cb *eb;
spin_lock_irqsave(&aun_queue_lock, flags);
- skb = skb_peek(&aun_queue);
- while (skb && skb != (struct sk_buff *)&aun_queue)
- {
- struct sk_buff *newskb = skb->next;
+ skb_queue_walk(&aun_queue, skb) {
eb = (struct ec_cb *)&skb->cb;
if (eb->seq == seq)
goto foundit;
-
- skb = newskb;
}
spin_unlock_irqrestore(&aun_queue_lock, flags);
printk(KERN_DEBUG "AUN: unknown sequence %ld\n", seq);
@@ -982,23 +977,18 @@ static void aun_data_available(struct sock *sk, int slen)
static void ab_cleanup(unsigned long h)
{
- struct sk_buff *skb;
+ struct sk_buff *skb, *n;
unsigned long flags;
spin_lock_irqsave(&aun_queue_lock, flags);
- skb = skb_peek(&aun_queue);
- while (skb && skb != (struct sk_buff *)&aun_queue)
- {
- struct sk_buff *newskb = skb->next;
+ skb_queue_walk_safe(&aun_queue, skb, n) {
struct ec_cb *eb = (struct ec_cb *)&skb->cb;
- if ((jiffies - eb->start) > eb->timeout)
- {
+ if ((jiffies - eb->start) > eb->timeout) {
tx_result(skb->sk, eb->cookie,
ECTYPE_TRANSMIT_NOT_PRESENT);
skb_unlink(skb, &aun_queue);
kfree_skb(skb);
}
- skb = newskb;
}
spin_unlock_irqrestore(&aun_queue_lock, flags);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 280352aba403..5a883affecd3 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -337,11 +337,6 @@ const struct header_ops eth_header_ops ____cacheline_aligned = {
void ether_setup(struct net_device *dev)
{
dev->header_ops = &eth_header_ops;
-#ifdef CONFIG_COMPAT_NET_DEV_OPS
- dev->change_mtu = eth_change_mtu;
- dev->set_mac_address = eth_mac_addr;
- dev->validate_addr = eth_validate_addr;
-#endif
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 9d26a3da37e5..5b919f7b45db 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -408,7 +408,7 @@ config INET_XFRM_MODE_BEET
config INET_LRO
bool "Large Receive Offload (ipv4/tcp)"
-
+ default y
---help---
Support for Large Receive Offload (ipv4/tcp).
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 170689681aa2..5abee4c97449 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1246,13 +1246,20 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
struct sk_buff **pp = NULL;
struct sk_buff *p;
struct iphdr *iph;
+ unsigned int hlen;
+ unsigned int off;
+ unsigned int id;
int flush = 1;
int proto;
- int id;
- iph = skb_gro_header(skb, sizeof(*iph));
- if (unlikely(!iph))
- goto out;
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*iph);
+ iph = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ iph = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!iph))
+ goto out;
+ }
proto = iph->protocol & (MAX_INET_PROTOS - 1);
@@ -1267,9 +1274,9 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto out_unlock;
- flush = ntohs(iph->tot_len) != skb_gro_len(skb) ||
- iph->frag_off != htons(IP_DF);
- id = ntohs(iph->id);
+ id = ntohl(*(u32 *)&iph->id);
+ flush = (u16)((ntohl(*(u32 *)iph) ^ skb_gro_len(skb)) | (id ^ IP_DF));
+ id >>= 16;
for (p = *head; p; p = p->next) {
struct iphdr *iph2;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 9070d11058e5..538d2a9a5115 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -986,9 +986,12 @@ fib_find_node(struct trie *t, u32 key)
static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
{
int wasfull;
- t_key cindex, key = tn->key;
+ t_key cindex, key;
struct tnode *tp;
+ preempt_disable();
+ key = tn->key;
+
while (tn != NULL && (tp = node_parent((struct node *)tn)) != NULL) {
cindex = tkey_extract_bits(key, tp->pos, tp->bits);
wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
@@ -1007,6 +1010,7 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
if (IS_TNODE(tn))
tn = (struct tnode *)resize(t, (struct tnode *)tn);
+ preempt_enable();
return (struct node *)tn;
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index c4c60e9f068a..28205e5bfa9b 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -784,8 +784,8 @@ static void rt_check_expire(void)
{
static unsigned int rover;
unsigned int i = rover, goal;
- struct rtable *rth, **rthp;
- unsigned long length = 0, samples = 0;
+ struct rtable *rth, *aux, **rthp;
+ unsigned long samples = 0;
unsigned long sum = 0, sum2 = 0;
u64 mult;
@@ -795,9 +795,9 @@ static void rt_check_expire(void)
goal = (unsigned int)mult;
if (goal > rt_hash_mask)
goal = rt_hash_mask + 1;
- length = 0;
for (; goal > 0; goal--) {
unsigned long tmo = ip_rt_gc_timeout;
+ unsigned long length;
i = (i + 1) & rt_hash_mask;
rthp = &rt_hash_table[i].chain;
@@ -809,8 +809,10 @@ static void rt_check_expire(void)
if (*rthp == NULL)
continue;
+ length = 0;
spin_lock_bh(rt_hash_lock_addr(i));
while ((rth = *rthp) != NULL) {
+ prefetch(rth->u.dst.rt_next);
if (rt_is_expired(rth)) {
*rthp = rth->u.dst.rt_next;
rt_free(rth);
@@ -819,33 +821,30 @@ static void rt_check_expire(void)
if (rth->u.dst.expires) {
/* Entry is expired even if it is in use */
if (time_before_eq(jiffies, rth->u.dst.expires)) {
+nofree:
tmo >>= 1;
rthp = &rth->u.dst.rt_next;
/*
- * Only bump our length if the hash
- * inputs on entries n and n+1 are not
- * the same, we only count entries on
+ * We only count entries on
* a chain with equal hash inputs once
* so that entries for different QOS
* levels, and other non-hash input
* attributes don't unfairly skew
* the length computation
*/
- if ((*rthp == NULL) ||
- !compare_hash_inputs(&(*rthp)->fl,
- &rth->fl))
- length += ONE;
+ for (aux = rt_hash_table[i].chain;;) {
+ if (aux == rth) {
+ length += ONE;
+ break;
+ }
+ if (compare_hash_inputs(&aux->fl, &rth->fl))
+ break;
+ aux = aux->u.dst.rt_next;
+ }
continue;
}
- } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) {
- tmo >>= 1;
- rthp = &rth->u.dst.rt_next;
- if ((*rthp == NULL) ||
- !compare_hash_inputs(&(*rthp)->fl,
- &rth->fl))
- length += ONE;
- continue;
- }
+ } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout))
+ goto nofree;
/* Cleanup aged off entries. */
*rthp = rth->u.dst.rt_next;
@@ -1068,7 +1067,6 @@ out: return 0;
static int rt_intern_hash(unsigned hash, struct rtable *rt, struct rtable **rp)
{
struct rtable *rth, **rthp;
- struct rtable *rthi;
unsigned long now;
struct rtable *cand, **candp;
u32 min_score;
@@ -1088,7 +1086,6 @@ restart:
}
rthp = &rt_hash_table[hash].chain;
- rthi = NULL;
spin_lock_bh(rt_hash_lock_addr(hash));
while ((rth = *rthp) != NULL) {
@@ -1134,17 +1131,6 @@ restart:
chain_length++;
rthp = &rth->u.dst.rt_next;
-
- /*
- * check to see if the next entry in the chain
- * contains the same hash input values as rt. If it does
- * This is where we will insert into the list, instead of
- * at the head. This groups entries that differ by aspects not
- * relvant to the hash function together, which we use to adjust
- * our chain length
- */
- if (*rthp && compare_hash_inputs(&(*rthp)->fl, &rt->fl))
- rthi = rth;
}
if (cand) {
@@ -1205,10 +1191,7 @@ restart:
}
}
- if (rthi)
- rt->u.dst.rt_next = rthi->u.dst.rt_next;
- else
- rt->u.dst.rt_next = rt_hash_table[hash].chain;
+ rt->u.dst.rt_next = rt_hash_table[hash].chain;
#if RT_CACHE_DEBUG >= 2
if (rt->u.dst.rt_next) {
@@ -1224,10 +1207,7 @@ restart:
* previous writes to rt are comitted to memory
* before making rt visible to other CPUS.
*/
- if (rthi)
- rcu_assign_pointer(rthi->u.dst.rt_next, rt);
- else
- rcu_assign_pointer(rt_hash_table[hash].chain, rt);
+ rcu_assign_pointer(rt_hash_table[hash].chain, rt);
spin_unlock_bh(rt_hash_lock_addr(hash));
*rp = rt;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 7a0f0b27bf1f..17b89c523f9d 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -439,12 +439,14 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
!tp->urg_data ||
before(tp->urg_seq, tp->copied_seq) ||
!before(tp->urg_seq, tp->rcv_nxt)) {
+ struct sk_buff *skb;
+
answ = tp->rcv_nxt - tp->copied_seq;
/* Subtract 1, if FIN is in queue. */
- if (answ && !skb_queue_empty(&sk->sk_receive_queue))
- answ -=
- tcp_hdr((struct sk_buff *)sk->sk_receive_queue.prev)->fin;
+ skb = skb_peek_tail(&sk->sk_receive_queue);
+ if (answ && skb)
+ answ -= tcp_hdr(skb)->fin;
} else
answ = tp->urg_seq - tp->copied_seq;
release_sock(sk);
@@ -1382,11 +1384,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
/* Next get a buffer. */
- skb = skb_peek(&sk->sk_receive_queue);
- do {
- if (!skb)
- break;
-
+ skb_queue_walk(&sk->sk_receive_queue, skb) {
/* Now that we have two receive queues this
* shouldn't happen.
*/
@@ -1403,8 +1401,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (tcp_hdr(skb)->fin)
goto found_fin_ok;
WARN_ON(!(flags & MSG_PEEK));
- skb = skb->next;
- } while (skb != (struct sk_buff *)&sk->sk_receive_queue);
+ }
/* Well, if we have backlog, try to process it now yet. */
@@ -2518,20 +2515,30 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
unsigned int thlen;
unsigned int flags;
unsigned int mss = 1;
+ unsigned int hlen;
+ unsigned int off;
int flush = 1;
int i;
- th = skb_gro_header(skb, sizeof(*th));
- if (unlikely(!th))
- goto out;
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*th);
+ th = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ th = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!th))
+ goto out;
+ }
thlen = th->doff * 4;
if (thlen < sizeof(*th))
goto out;
- th = skb_gro_header(skb, thlen);
- if (unlikely(!th))
- goto out;
+ hlen = off + thlen;
+ if (skb_gro_header_hard(skb, hlen)) {
+ th = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!th))
+ goto out;
+ }
skb_gro_pull(skb, thlen);
@@ -2544,7 +2551,7 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
th2 = tcp_hdr(p);
- if ((th->source ^ th2->source) | (th->dest ^ th2->dest)) {
+ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
NAPI_GRO_CB(p)->same_flow = 0;
continue;
}
@@ -2559,14 +2566,14 @@ found:
flush |= flags & TCP_FLAG_CWR;
flush |= (flags ^ tcp_flag_word(th2)) &
~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH);
- flush |= (th->ack_seq ^ th2->ack_seq) | (th->window ^ th2->window);
- for (i = sizeof(*th); !flush && i < thlen; i += 4)
+ flush |= th->ack_seq ^ th2->ack_seq;
+ for (i = sizeof(*th); i < thlen; i += 4)
flush |= *(u32 *)((u8 *)th + i) ^
*(u32 *)((u8 *)th2 + i);
mss = skb_shinfo(p)->gso_size;
- flush |= (len > mss) | !len;
+ flush |= (len - 1) >= mss;
flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq);
if (flush || skb_gro_receive(head, skb)) {
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index eeb8a92aa416..ba34a23c1bfb 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4426,7 +4426,7 @@ drop:
}
__skb_queue_head(&tp->out_of_order_queue, skb);
} else {
- struct sk_buff *skb1 = tp->out_of_order_queue.prev;
+ struct sk_buff *skb1 = skb_peek_tail(&tp->out_of_order_queue);
u32 seq = TCP_SKB_CB(skb)->seq;
u32 end_seq = TCP_SKB_CB(skb)->end_seq;
@@ -4443,15 +4443,18 @@ drop:
}
/* Find place to insert this segment. */
- do {
+ while (1) {
if (!after(TCP_SKB_CB(skb1)->seq, seq))
break;
- } while ((skb1 = skb1->prev) !=
- (struct sk_buff *)&tp->out_of_order_queue);
+ if (skb_queue_is_first(&tp->out_of_order_queue, skb1)) {
+ skb1 = NULL;
+ break;
+ }
+ skb1 = skb_queue_prev(&tp->out_of_order_queue, skb1);
+ }
/* Do skb overlap to previous one? */
- if (skb1 != (struct sk_buff *)&tp->out_of_order_queue &&
- before(seq, TCP_SKB_CB(skb1)->end_seq)) {
+ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) {
if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
/* All the bits are present. Drop. */
__kfree_skb(skb);
@@ -4463,24 +4466,41 @@ drop:
tcp_dsack_set(sk, seq,
TCP_SKB_CB(skb1)->end_seq);
} else {
- skb1 = skb1->prev;
+ if (skb_queue_is_first(&tp->out_of_order_queue,
+ skb1))
+ skb1 = NULL;
+ else
+ skb1 = skb_queue_prev(
+ &tp->out_of_order_queue,
+ skb1);
}
}
- __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
+ if (!skb1)
+ __skb_queue_head(&tp->out_of_order_queue, skb);
+ else
+ __skb_queue_after(&tp->out_of_order_queue, skb1, skb);
/* And clean segments covered by new one as whole. */
- while ((skb1 = skb->next) !=
- (struct sk_buff *)&tp->out_of_order_queue &&
- after(end_seq, TCP_SKB_CB(skb1)->seq)) {
- if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
+ if (skb1 && !skb_queue_is_last(&tp->out_of_order_queue, skb1)) {
+ struct sk_buff *n;
+
+ skb1 = skb_queue_next(&tp->out_of_order_queue, skb1);
+ skb_queue_walk_from_safe(&tp->out_of_order_queue,
+ skb1, n) {
+ if (!after(end_seq, TCP_SKB_CB(skb1)->seq))
+ break;
+ if (before(end_seq,
+ TCP_SKB_CB(skb1)->end_seq)) {
+ tcp_dsack_extend(sk,
+ TCP_SKB_CB(skb1)->seq,
+ end_seq);
+ break;
+ }
+ __skb_unlink(skb1, &tp->out_of_order_queue);
tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
- end_seq);
- break;
+ TCP_SKB_CB(skb1)->end_seq);
+ __kfree_skb(skb1);
}
- __skb_unlink(skb1, &tp->out_of_order_queue);
- tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
- TCP_SKB_CB(skb1)->end_seq);
- __kfree_skb(skb1);
}
add_sack:
@@ -4492,7 +4512,10 @@ add_sack:
static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
struct sk_buff_head *list)
{
- struct sk_buff *next = skb->next;
+ struct sk_buff *next = NULL;
+
+ if (!skb_queue_is_last(list, skb))
+ next = skb_queue_next(list, skb);
__skb_unlink(skb, list);
__kfree_skb(skb);
@@ -4503,6 +4526,9 @@ static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb,
/* Collapse contiguous sequence of skbs head..tail with
* sequence numbers start..end.
+ *
+ * If tail is NULL, this means until the end of the list.
+ *
* Segments with FIN/SYN are not collapsed (only because this
* simplifies code)
*/
@@ -4511,15 +4537,23 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
struct sk_buff *head, struct sk_buff *tail,
u32 start, u32 end)
{
- struct sk_buff *skb;
+ struct sk_buff *skb, *n;
+ bool end_of_skbs;
/* First, check that queue is collapsible and find
* the point where collapsing can be useful. */
- for (skb = head; skb != tail;) {
+ skb = head;
+restart:
+ end_of_skbs = true;
+ skb_queue_walk_from_safe(list, skb, n) {
+ if (skb == tail)
+ break;
/* No new bits? It is possible on ofo queue. */
if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
skb = tcp_collapse_one(sk, skb, list);
- continue;
+ if (!skb)
+ break;
+ goto restart;
}
/* The first skb to collapse is:
@@ -4529,16 +4563,24 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
*/
if (!tcp_hdr(skb)->syn && !tcp_hdr(skb)->fin &&
(tcp_win_from_space(skb->truesize) > skb->len ||
- before(TCP_SKB_CB(skb)->seq, start) ||
- (skb->next != tail &&
- TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb->next)->seq)))
+ before(TCP_SKB_CB(skb)->seq, start))) {
+ end_of_skbs = false;
break;
+ }
+
+ if (!skb_queue_is_last(list, skb)) {
+ struct sk_buff *next = skb_queue_next(list, skb);
+ if (next != tail &&
+ TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(next)->seq) {
+ end_of_skbs = false;
+ break;
+ }
+ }
/* Decided to skip this, advance start seq. */
start = TCP_SKB_CB(skb)->end_seq;
- skb = skb->next;
}
- if (skb == tail || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin)
+ if (end_of_skbs || tcp_hdr(skb)->syn || tcp_hdr(skb)->fin)
return;
while (before(start, end)) {
@@ -4583,7 +4625,8 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
}
if (!before(start, TCP_SKB_CB(skb)->end_seq)) {
skb = tcp_collapse_one(sk, skb, list);
- if (skb == tail ||
+ if (!skb ||
+ skb == tail ||
tcp_hdr(skb)->syn ||
tcp_hdr(skb)->fin)
return;
@@ -4610,17 +4653,21 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
head = skb;
for (;;) {
- skb = skb->next;
+ struct sk_buff *next = NULL;
+
+ if (!skb_queue_is_last(&tp->out_of_order_queue, skb))
+ next = skb_queue_next(&tp->out_of_order_queue, skb);
+ skb = next;
/* Segment is terminated when we see gap or when
* we are at the end of all the queue. */
- if (skb == (struct sk_buff *)&tp->out_of_order_queue ||
+ if (!skb ||
after(TCP_SKB_CB(skb)->seq, end) ||
before(TCP_SKB_CB(skb)->end_seq, start)) {
tcp_collapse(sk, &tp->out_of_order_queue,
head, skb, start, end);
head = skb;
- if (skb == (struct sk_buff *)&tp->out_of_order_queue)
+ if (!skb)
break;
/* Start new segment */
start = TCP_SKB_CB(skb)->seq;
@@ -4681,10 +4728,11 @@ static int tcp_prune_queue(struct sock *sk)
tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
tcp_collapse_ofo_queue(sk);
- tcp_collapse(sk, &sk->sk_receive_queue,
- sk->sk_receive_queue.next,
- (struct sk_buff *)&sk->sk_receive_queue,
- tp->copied_seq, tp->rcv_nxt);
+ if (!skb_queue_empty(&sk->sk_receive_queue))
+ tcp_collapse(sk, &sk->sk_receive_queue,
+ skb_peek(&sk->sk_receive_queue),
+ NULL,
+ tp->copied_seq, tp->rcv_nxt);
sk_mem_reclaim(sk);
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 61f55386a236..b6215be0963f 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -817,13 +817,20 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
struct sk_buff *p;
struct ipv6hdr *iph;
unsigned int nlen;
+ unsigned int hlen;
+ unsigned int off;
int flush = 1;
int proto;
__wsum csum;
- iph = skb_gro_header(skb, sizeof(*iph));
- if (unlikely(!iph))
- goto out;
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*iph);
+ iph = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ iph = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!iph))
+ goto out;
+ }
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 1394ddb6e35c..032a5ec391c5 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -137,6 +137,7 @@ static struct rt6_info ip6_null_entry_template = {
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+ .rt6i_protocol = RTPROT_KERNEL,
.rt6i_metric = ~(u32) 0,
.rt6i_ref = ATOMIC_INIT(1),
};
@@ -159,6 +160,7 @@ static struct rt6_info ip6_prohibit_entry_template = {
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+ .rt6i_protocol = RTPROT_KERNEL,
.rt6i_metric = ~(u32) 0,
.rt6i_ref = ATOMIC_INIT(1),
};
@@ -176,6 +178,7 @@ static struct rt6_info ip6_blk_hole_entry_template = {
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+ .rt6i_protocol = RTPROT_KERNEL,
.rt6i_metric = ~(u32) 0,
.rt6i_ref = ATOMIC_INIT(1),
};
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index d9dd94b6bf66..ea37741062a9 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -941,7 +941,8 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
return 0;
}
-struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
{
struct ipv6hdr *iph = skb_gro_network_header(skb);
@@ -961,9 +962,8 @@ struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb)
return tcp_gro_receive(head, skb);
}
-EXPORT_SYMBOL(tcp6_gro_receive);
-int tcp6_gro_complete(struct sk_buff *skb)
+static int tcp6_gro_complete(struct sk_buff *skb)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
@@ -974,7 +974,6 @@ int tcp6_gro_complete(struct sk_buff *skb)
return tcp_gro_complete(skb);
}
-EXPORT_SYMBOL(tcp6_gro_complete);
static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
u32 ts, struct tcp_md5sig_key *key, int rst)
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index 2562ebc1b22c..7af2e74deda8 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -982,17 +982,12 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
{
struct sk_buff *tx_skb;
struct sk_buff *skb;
- int count;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- /* Initialize variables */
- count = skb_queue_len(&self->wx_list);
-
/* Resend unacknowledged frame(s) */
- skb = skb_peek(&self->wx_list);
- while (skb != NULL) {
+ skb_queue_walk(&self->wx_list, skb) {
irlap_wait_min_turn_around(self, &self->qos_tx);
/* We copy the skb to be retransmitted since we will have to
@@ -1011,21 +1006,12 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
/*
* Set poll bit on the last frame retransmitted
*/
- if (count-- == 1)
+ if (skb_queue_is_last(&self->wx_list, skb))
tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
else
tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */
irlap_send_i_frame(self, tx_skb, command);
-
- /*
- * If our skb is the last buffer in the list, then
- * we are finished, if not, move to the next sk-buffer
- */
- if (skb == skb_peek_tail(&self->wx_list))
- skb = NULL;
- else
- skb = skb->next;
}
#if 0 /* Not yet */
/*
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 3477624a4906..c6bab39b018e 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -79,10 +79,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
if (unlikely(!ev->ind_prim && !ev->cfm_prim)) {
/* indicate or confirm not required */
- /* XXX this is not very pretty, perhaps we should store
- * XXX indicate/confirm-needed state in the llc_conn_state_ev
- * XXX control block of the SKB instead? -DaveM
- */
if (!skb->next)
goto out_kfree_skb;
goto out_skb_put;
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index e7682fe1c590..11c72311f35b 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -52,14 +52,6 @@ static const struct file_operations name## _ops = { \
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
local->hw.conf.channel->center_freq);
-DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
- local->hw.wiphy->rts_threshold);
-DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
- local->hw.wiphy->frag_threshold);
-DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
- local->hw.wiphy->retry_short);
-DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
- local->hw.wiphy->retry_long);
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
local->total_ps_buffered);
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
@@ -303,10 +295,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
local->debugfs.keys = debugfs_create_dir("keys", phyd);
DEBUGFS_ADD(frequency);
- DEBUGFS_ADD(rts_threshold);
- DEBUGFS_ADD(fragmentation_threshold);
- DEBUGFS_ADD(short_retry_limit);
- DEBUGFS_ADD(long_retry_limit);
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(tsf);
@@ -359,10 +347,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
void debugfs_hw_del(struct ieee80211_local *local)
{
DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(rts_threshold);
- DEBUGFS_DEL(fragmentation_threshold);
- DEBUGFS_DEL(short_retry_limit);
- DEBUGFS_DEL(long_retry_limit);
DEBUGFS_DEL(total_ps_buffered);
DEBUGFS_DEL(wep_iv);
DEBUGFS_DEL(tsf);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index c236079ed38a..0b30277eb366 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -535,9 +535,9 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
bssid = ifibss->bssid;
bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid,
ifibss->ssid, ifibss->ssid_len,
- capability,
WLAN_CAPABILITY_IBSS |
- WLAN_CAPABILITY_PRIVACY);
+ WLAN_CAPABILITY_PRIVACY,
+ capability);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (bss)
@@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work)
struct ieee80211_if_ibss *ifibss;
struct sk_buff *skb;
+ if (WARN_ON(local->suspended))
+ return;
+
if (!netif_running(sdata->dev))
return;
@@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data)
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
+ if (local->quiescing) {
+ ifibss->timer_running = true;
+ return;
+ }
+
set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
queue_work(local->hw.workqueue, &ifibss->work);
}
+#ifdef CONFIG_PM
+void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ cancel_work_sync(&ifibss->work);
+ if (del_timer_sync(&ifibss->timer))
+ ifibss->timer_running = true;
+}
+
+void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ if (ifibss->timer_running) {
+ add_timer(&ifibss->timer);
+ ifibss->timer_running = false;
+ }
+}
+#endif
+
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9d1514727f6e..c088c46704a3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -293,6 +293,7 @@ struct ieee80211_if_managed {
int auth_tries; /* retries for auth req */
int assoc_tries; /* retries for assoc req */
+ unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
unsigned long request;
@@ -333,6 +334,9 @@ struct ieee80211_if_ibss {
unsigned long request;
unsigned long last_scan_completed;
+
+ bool timer_running;
+
bool fixed_bssid;
bool fixed_channel;
@@ -358,6 +362,8 @@ struct ieee80211_if_mesh {
struct timer_list mesh_path_timer;
struct sk_buff_head skb_queue;
+ unsigned long timers_running;
+
bool housekeeping;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -609,6 +615,21 @@ struct ieee80211_local {
unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats;
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
+
+ /*
+ * suspended is true if we finished all the suspend _and_ we have
+ * not yet come up from resume. This is to be used by mac80211
+ * to ensure driver sanity during suspend and mac80211's own
+ * sanity. It can eventually be used for WoW as well.
+ */
+ bool suspended;
+
+ /*
+ * quiescing is true during the suspend process _only_ to
+ * ease timer cancelling etc.
+ */
+ bool quiescing;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -758,10 +779,6 @@ struct ieee80211_local {
struct dentry *rcdir;
struct dentry *rcname;
struct dentry *frequency;
- struct dentry *rts_threshold;
- struct dentry *fragmentation_threshold;
- struct dentry *short_retry_limit;
- struct dentry *long_retry_limit;
struct dentry *total_ps_buffered;
struct dentry *wep_iv;
struct dentry *tsf;
@@ -938,6 +955,11 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
int ieee80211_max_network_latency(struct notifier_block *nb,
unsigned long data, void *dummy);
+void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_sw_ie *sw_elem,
+ struct ieee80211_bss *bss);
+void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -950,6 +972,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
/* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work);
@@ -960,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_scan_results(struct ieee80211_local *local,
struct iw_request_info *info,
char *buf, size_t len);
+void ieee80211_scan_cancel(struct ieee80211_local *local);
ieee80211_rx_result
ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
@@ -1035,14 +1060,6 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len);
-void ieee80211_chswitch_timer(unsigned long data);
-void ieee80211_chswitch_work(struct work_struct *work);
-void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss);
-void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- u16 capab_info, u8 *pwr_constr_elem,
- u8 pwr_constr_elem_len);
/* Suspend/resume and hw reconfiguration */
int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1068,8 +1085,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
-extern const unsigned char rfc1042_header[6];
-extern const unsigned char bridge_tunnel_header[6];
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type);
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 827ea8e6ee0a..ce267565e180 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -320,7 +320,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
case ALG_TKIP:
key->conf.iv_len = TKIP_IV_LEN;
key->conf.icv_len = TKIP_ICV_LEN;
- if (seq && seq_len == 6) {
+ if (seq) {
for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
key->u.tkip.rx[i].iv32 =
get_unaligned_le32(&seq[2]);
@@ -332,7 +332,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
case ALG_CCMP:
key->conf.iv_len = CCMP_HDR_LEN;
key->conf.icv_len = CCMP_MIC_LEN;
- if (seq && seq_len == CCMP_PN_LEN) {
+ if (seq) {
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
for (j = 0; j < CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
@@ -342,7 +342,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
case ALG_AES_CMAC:
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie);
- if (seq && seq_len == 6)
+ if (seq)
for (j = 0; j < 6; j++)
key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
break;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 76df5eabf268..e37770ced53c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -219,18 +219,26 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed)
{
struct ieee80211_local *local = sdata->local;
+ static const u8 zero[ETH_ALEN] = { 0 };
if (!changed)
return;
- if (sdata->vif.type == NL80211_IFTYPE_STATION)
- sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
- else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ /*
+ * While not associated, claim a BSSID of all-zeroes
+ * so that drivers don't do any weird things with the
+ * BSSID at that time.
+ */
+ if (sdata->vif.bss_conf.assoc)
+ sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
+ else
+ sdata->vif.bss_conf.bssid = zero;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
else if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata->vif.bss_conf.bssid = sdata->dev->dev_addr;
else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- static const u8 zero[ETH_ALEN] = { 0 };
sdata->vif.bss_conf.bssid = zero;
} else {
WARN_ON(1);
@@ -727,9 +735,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
* +-------------------------+
*
*/
- priv_size = ((sizeof(struct ieee80211_local) +
- NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) +
- priv_data_len;
+ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
wiphy = wiphy_new(&mac80211_config_ops, priv_size);
@@ -746,9 +752,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.wiphy = wiphy;
- local->hw.priv = (char *)local +
- ((sizeof(struct ieee80211_local) +
- NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
+ local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
BUG_ON(!ops->tx);
BUG_ON(!ops->start);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 9000b01a1671..fc712e60705d 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -21,6 +21,9 @@
#define CAPAB_OFFSET 17
#define ACCEPT_PLINKS 0x80
+#define TMR_RUNNING_HK 0
+#define TMR_RUNNING_MP 1
+
int mesh_allocated;
static struct kmem_cache *rm_cache;
@@ -45,6 +48,12 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
ifmsh->housekeeping = true;
+
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+ return;
+ }
+
queue_work(local->hw.workqueue, &ifmsh->work);
}
@@ -343,6 +352,11 @@ static void ieee80211_mesh_path_timer(unsigned long data)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+ return;
+ }
+
queue_work(local->hw.workqueue, &ifmsh->work);
}
@@ -424,6 +438,32 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
}
+#ifdef CONFIG_PM
+void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ /* might restart the timer but that doesn't matter */
+ cancel_work_sync(&ifmsh->work);
+
+ /* use atomic bitops in case both timers fire at the same time */
+
+ if (del_timer_sync(&ifmsh->housekeeping_timer))
+ set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+ if (del_timer_sync(&ifmsh->mesh_path_timer))
+ set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+}
+
+void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
+ add_timer(&ifmsh->housekeeping_timer);
+ if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
+ add_timer(&ifmsh->mesh_path_timer);
+}
+#endif
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index d891d7ddccd7..c7d72819cdd2 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -191,12 +191,8 @@ struct mesh_rmc {
#define PLINK_CATEGORY 30
#define MESH_PATH_SEL_CATEGORY 32
-/* Mesh Header Flags */
-#define IEEE80211S_FLAGS_AE 0x3
-
/* Public interfaces */
/* Various */
-int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
@@ -267,6 +263,8 @@ void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta);
void mesh_path_discard_frame(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
+void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
+void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
#ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated;
@@ -294,10 +292,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath)
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
+void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
+void mesh_plink_quiesce(struct sta_info *sta);
+void mesh_plink_restart(struct sta_info *sta);
#else
#define mesh_allocated 0
static inline void
ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
+static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
+{}
+static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
+{}
+static inline void mesh_plink_quiesce(struct sta_info *sta) {}
+static inline void mesh_plink_restart(struct sta_info *sta) {}
#endif
#endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 60b35accda91..003cb470ac84 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -836,8 +836,14 @@ void mesh_path_timer(unsigned long data)
mpath = rcu_dereference(mpath);
if (!mpath)
goto endmpathtimer;
- spin_lock_bh(&mpath->state_lock);
sdata = mpath->sdata;
+
+ if (sdata->local->quiescing) {
+ rcu_read_unlock();
+ return;
+ }
+
+ spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_RESOLVED ||
(!(mpath->flags & MESH_PATH_RESOLVING)))
mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index a8bbdeca013a..cb14253587f1 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -266,6 +266,11 @@ static void mesh_plink_timer(unsigned long data)
*/
sta = (struct sta_info *) data;
+ if (sta->sdata->local->quiescing) {
+ sta->plink_timer_was_running = true;
+ return;
+ }
+
spin_lock_bh(&sta->lock);
if (sta->ignore_plink_timer) {
sta->ignore_plink_timer = false;
@@ -322,6 +327,22 @@ static void mesh_plink_timer(unsigned long data)
}
}
+#ifdef CONFIG_PM
+void mesh_plink_quiesce(struct sta_info *sta)
+{
+ if (del_timer_sync(&sta->plink_timer))
+ sta->plink_timer_was_running = true;
+}
+
+void mesh_plink_restart(struct sta_info *sta)
+{
+ if (sta->plink_timer_was_running) {
+ add_timer(&sta->plink_timer);
+ sta->plink_timer_was_running = false;
+ }
+}
+#endif
+
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
{
sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index ae030688771f..509469cb9265 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -33,10 +33,13 @@
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_PROBE_WAIT (HZ / 20)
+#define IEEE80211_PROBE_WAIT (HZ / 5)
#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
+#define TMR_RUNNING_TIMER 0
+#define TMR_RUNNING_CHANSW 1
+
/* utils */
static int ecw2cw(int ecw)
{
@@ -121,10 +124,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- channel_type = NL80211_CHAN_HT40PLUS;
+ if (!(local->hw.conf.channel->flags &
+ IEEE80211_CHAN_NO_HT40PLUS))
+ channel_type = NL80211_CHAN_HT40PLUS;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- channel_type = NL80211_CHAN_HT40MINUS;
+ if (!(local->hw.conf.channel->flags &
+ IEEE80211_CHAN_NO_HT40MINUS))
+ channel_type = NL80211_CHAN_HT40MINUS;
break;
}
}
@@ -349,13 +356,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
+ if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
+ if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
@@ -482,6 +489,108 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
ieee80211_tx_skb(sdata, skb, 0);
}
+/* spectrum management related things */
+static void ieee80211_chswitch_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
+ struct ieee80211_bss *bss;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ if (!netif_running(sdata->dev))
+ return;
+
+ bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
+ sdata->local->hw.conf.channel->center_freq,
+ ifmgd->ssid, ifmgd->ssid_len);
+ if (!bss)
+ goto exit;
+
+ sdata->local->oper_channel = sdata->local->csa_channel;
+ /* XXX: shouldn't really modify cfg80211-owned data! */
+ if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
+ bss->cbss.channel = sdata->local->oper_channel;
+
+ ieee80211_rx_bss_put(sdata->local, bss);
+exit:
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+}
+
+static void ieee80211_chswitch_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ if (sdata->local->quiescing) {
+ set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
+ return;
+ }
+
+ queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+}
+
+void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_sw_ie *sw_elem,
+ struct ieee80211_bss *bss)
+{
+ struct ieee80211_channel *new_ch;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
+
+ if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
+ return;
+
+ if (sdata->local->sw_scanning || sdata->local->hw_scanning)
+ return;
+
+ /* Disregard subsequent beacons if we are already running a timer
+ processing a CSA */
+
+ if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
+ return;
+
+ new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+ if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+ return;
+
+ sdata->local->csa_channel = new_ch;
+
+ if (sw_elem->count <= 1) {
+ queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+ } else {
+ ieee80211_stop_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ mod_timer(&ifmgd->chswitch_timer,
+ jiffies +
+ msecs_to_jiffies(sw_elem->count *
+ bss->cbss.beacon_interval));
+ }
+}
+
+static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+ u16 capab_info, u8 *pwr_constr_elem,
+ u8 pwr_constr_elem_len)
+{
+ struct ieee80211_conf *conf = &sdata->local->hw.conf;
+
+ if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+ return;
+
+ /* Power constraint IE length should be 1 octet */
+ if (pwr_constr_elem_len != 1)
+ return;
+
+ if ((*pwr_constr_elem <= conf->channel->max_power) &&
+ (*pwr_constr_elem != sdata->local->power_constr_level)) {
+ sdata->local->power_constr_level = *pwr_constr_elem;
+ ieee80211_hw_config(sdata->local, 0);
+ }
+}
+
/* powersave */
static void ieee80211_enable_ps(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
@@ -613,6 +722,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{
struct ieee80211_local *local = (void *) data;
+ if (local->quiescing)
+ return;
+
queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
}
@@ -865,6 +977,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
* changed or not.
*/
bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+
+ /* And the BSSID changed - we're associated now */
+ bss_info_changed |= BSS_CHANGED_BSSID;
+
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
/* will be same as sdata */
@@ -1064,6 +1180,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
}
ieee80211_hw_config(local, config_changed);
+
+ /* And the BSSID changed -- not very interesting here */
+ changed |= BSS_CHANGED_BSSID;
ieee80211_bss_info_change_notify(sdata, changed);
rcu_read_lock();
@@ -1270,8 +1389,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
ifmgd->ssid_len, NULL, 0);
+ mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
goto unlock;
-
}
if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) {
@@ -1280,15 +1399,16 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
ifmgd->ssid_len, NULL, 0);
}
+ if (!disassoc)
+ mod_timer(&ifmgd->timer,
+ jiffies + IEEE80211_MONITORING_INTERVAL);
+
unlock:
rcu_read_unlock();
if (disassoc)
ieee80211_set_disassoc(sdata, true, true,
WLAN_REASON_PREV_AUTH_NOT_VALID);
- else
- mod_timer(&ifmgd->timer, jiffies +
- IEEE80211_MONITORING_INTERVAL);
}
@@ -1732,7 +1852,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
(memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) {
struct ieee80211_channel_sw_ie *sw_elem =
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
- ieee80211_process_chanswitch(sdata, sw_elem, bss);
+ ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
}
ieee80211_rx_bss_put(local, bss);
@@ -1820,6 +1940,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
+ if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: cancelling probereq poll due "
+ "to a received beacon\n", sdata->dev->name);
+ }
+#endif
+ ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+ }
+
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
len - baselen, &elems,
@@ -1829,16 +1959,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
ifmgd->aid);
- ncrc = crc32_be(ncrc, (void *)&directed_tim, sizeof(directed_tim));
+ if (ncrc != ifmgd->beacon_crc) {
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
+ true);
- if (ncrc == ifmgd->beacon_crc)
- return;
- ifmgd->beacon_crc = ncrc;
-
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
-
- ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
- elems.wmm_param_len);
+ ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
+ elems.wmm_param_len);
+ }
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
if (directed_tim) {
@@ -1863,6 +1990,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (ncrc == ifmgd->beacon_crc)
+ return;
+ ifmgd->beacon_crc = ncrc;
+
if (elems.erp_info && elems.erp_info_len >= 1) {
erp_valid = true;
erp_value = elems.erp_info[0];
@@ -1997,6 +2128,11 @@ static void ieee80211_sta_timer(unsigned long data)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
+ return;
+ }
+
set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
queue_work(local->hw.workqueue, &ifmgd->work);
}
@@ -2129,6 +2265,17 @@ static void ieee80211_sta_work(struct work_struct *work)
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return;
+
+ /*
+ * Nothing should have been stuffed into the workqueue during
+ * the suspend->resume cycle. If this WARN is seen then there
+ * is a bug with either the driver suspend or something in
+ * mac80211 stuffing into the workqueue which we haven't yet
+ * cleared during mac80211's suspend cycle.
+ */
+ if (WARN_ON(local->suspended))
+ return;
+
ifmgd = &sdata->u.mgd;
while ((skb = skb_dequeue(&ifmgd->skb_queue)))
@@ -2196,6 +2343,38 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
}
}
+#ifdef CONFIG_PM
+void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ /*
+ * we need to use atomic bitops for the running bits
+ * only because both timers might fire at the same
+ * time -- the code here is properly synchronised.
+ */
+
+ cancel_work_sync(&ifmgd->work);
+ cancel_work_sync(&ifmgd->beacon_loss_work);
+ if (del_timer_sync(&ifmgd->timer))
+ set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
+
+ cancel_work_sync(&ifmgd->chswitch_work);
+ if (del_timer_sync(&ifmgd->chswitch_timer))
+ set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
+}
+
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
+ add_timer(&ifmgd->timer);
+ if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
+ add_timer(&ifmgd->chswitch_timer);
+}
+#endif
+
/* interface setup */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
@@ -2310,9 +2489,6 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
}
- if (netif_running(sdata->dev))
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-
return ieee80211_sta_commit(sdata);
}
@@ -2321,6 +2497,13 @@ int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ if (len == 0 && ifmgd->extra_ie_len == 0)
+ return -EALREADY;
+
+ if (len == ifmgd->extra_ie_len && ifmgd->extra_ie &&
+ memcmp(ifmgd->extra_ie, ie, len) == 0)
+ return -EALREADY;
+
kfree(ifmgd->extra_ie);
if (len == 0) {
ifmgd->extra_ie = NULL;
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9d3d89abbb57..7a549f9deb96 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -2,6 +2,7 @@
#include <net/rtnetlink.h>
#include "ieee80211_i.h"
+#include "mesh.h"
#include "driver-ops.h"
#include "led.h"
@@ -13,11 +14,30 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct sta_info *sta;
unsigned long flags;
+ ieee80211_scan_cancel(local);
+
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+ /* flush out all packets */
+ synchronize_net();
+
+ local->quiescing = true;
+ /* make quiescing visible to timers everywhere */
+ mb();
+
flush_workqueue(local->hw.workqueue);
+ /* Don't try to run timers while suspended. */
+ del_timer_sync(&local->sta_cleanup);
+
+ /*
+ * Note that this particular timer doesn't need to be
+ * restarted at resume.
+ */
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+ del_timer_sync(&local->dynamic_ps_timer);
+
/* disable keys */
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata);
@@ -35,10 +55,20 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
rcu_read_unlock();
+ /* flush again, in case driver queued work */
+ flush_workqueue(local->hw.workqueue);
+
+ /* stop hardware - this must stop RX */
+ if (local->open_count) {
+ ieee80211_led_radio(local, false);
+ drv_stop(local);
+ }
+
/* remove STAs */
- if (local->ops->sta_notify) {
- spin_lock_irqsave(&local->sta_lock, flags);
- list_for_each_entry(sta, &local->sta_list, list) {
+ spin_lock_irqsave(&local->sta_lock, flags);
+ list_for_each_entry(sta, &local->sta_list, list) {
+ if (local->ops->sta_notify) {
+ sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
@@ -47,29 +77,43 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
&sta->sta);
}
- spin_unlock_irqrestore(&local->sta_lock, flags);
+
+ mesh_plink_quiesce(sta);
}
+ spin_unlock_irqrestore(&local->sta_lock, flags);
/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- sdata->vif.type != NL80211_IFTYPE_MONITOR &&
- netif_running(sdata->dev)) {
- conf.vif = &sdata->vif;
- conf.type = sdata->vif.type;
- conf.mac_addr = sdata->dev->dev_addr;
- drv_remove_interface(local, &conf);
+ switch(sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ ieee80211_sta_quiesce(sdata);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ieee80211_ibss_quiesce(sdata);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ ieee80211_mesh_quiesce(sdata);
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* don't tell driver about this */
+ continue;
+ default:
+ break;
}
- }
- /* flush again, in case driver queued work */
- flush_workqueue(local->hw.workqueue);
+ if (!netif_running(sdata->dev))
+ continue;
- /* stop hardware */
- if (local->open_count) {
- ieee80211_led_radio(local, false);
- drv_stop(local);
+ conf.vif = &sdata->vif;
+ conf.type = sdata->vif.type;
+ conf.mac_addr = sdata->dev->dev_addr;
+ drv_remove_interface(local, &conf);
}
+
+ local->suspended = true;
+ local->quiescing = false;
+
return 0;
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index f962bd1b16e2..6a9b8e63a6bf 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1247,93 +1247,12 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
}
static int
-ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
+__ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- u16 hdrlen, ethertype;
- u8 *payload;
- u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN] __aligned(2);
- struct sk_buff *skb = rx->skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
- return -1;
-
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
-
- /* convert IEEE 802.11 header + possible LLC headers into Ethernet
- * header
- * IEEE 802.11 address fields:
- * ToDS FromDS Addr1 Addr2 Addr3 Addr4
- * 0 0 DA SA BSSID n/a
- * 0 1 DA BSSID SA n/a
- * 1 0 BSSID SA DA n/a
- * 1 1 RA TA DA SA
- */
- memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
- memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
-
- switch (hdr->frame_control &
- cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case cpu_to_le16(IEEE80211_FCTL_TODS):
- if (unlikely(sdata->vif.type != NL80211_IFTYPE_AP &&
- sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
- return -1;
- break;
- case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
- if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
- return -1;
- if (ieee80211_vif_is_mesh(&sdata->vif)) {
- struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *)
- (skb->data + hdrlen);
- hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
- if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
- memcpy(dst, meshdr->eaddr1, ETH_ALEN);
- memcpy(src, meshdr->eaddr2, ETH_ALEN);
- }
- }
- break;
- case cpu_to_le16(IEEE80211_FCTL_FROMDS):
- if (sdata->vif.type != NL80211_IFTYPE_STATION ||
- (is_multicast_ether_addr(dst) &&
- !compare_ether_addr(src, dev->dev_addr)))
- return -1;
- break;
- case cpu_to_le16(0):
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
- return -1;
- break;
- }
-
- if (unlikely(skb->len - hdrlen < 8))
- return -1;
-
- payload = skb->data + hdrlen;
- ethertype = (payload[6] << 8) | payload[7];
-
- if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
- compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation and
- * replace EtherType */
- skb_pull(skb, hdrlen + 6);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- } else {
- struct ethhdr *ehdr;
- __be16 len;
-
- skb_pull(skb, hdrlen);
- len = htons(skb->len);
- ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
- memcpy(ehdr->h_dest, dst, ETH_ALEN);
- memcpy(ehdr->h_source, src, ETH_ALEN);
- ehdr->h_proto = len;
- }
- return 0;
+ return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
}
/*
@@ -1472,7 +1391,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
if (!(rx->flags & IEEE80211_RX_AMSDU))
return RX_CONTINUE;
- err = ieee80211_data_to_8023(rx);
+ err = __ieee80211_data_to_8023(rx);
if (unlikely(err))
return RX_DROP_UNUSABLE;
@@ -1658,7 +1577,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR;
- err = ieee80211_data_to_8023(rx);
+ err = __ieee80211_data_to_8023(rx);
if (unlikely(err))
return RX_DROP_UNUSABLE;
@@ -1846,6 +1765,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sizeof(mgmt->u.action.u.chan_switch)))
return RX_DROP_MONITOR;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return RX_DROP_MONITOR;
+
if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
return RX_DROP_MONITOR;
@@ -1856,7 +1778,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (!bss)
return RX_DROP_MONITOR;
- ieee80211_process_chanswitch(sdata,
+ ieee80211_sta_process_chanswitch(sdata,
&mgmt->u.action.u.chan_switch.sw_elem, bss);
ieee80211_rx_bss_put(local, bss);
break;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e65d74ba404b..2a8d09ad17ff 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -631,3 +631,21 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->scan_mtx);
return ret;
}
+
+void ieee80211_scan_cancel(struct ieee80211_local *local)
+{
+ bool swscan;
+
+ cancel_delayed_work_sync(&local->scan_work);
+
+ /*
+ * Only call this function when a scan can't be
+ * queued -- mostly at suspend under RTNL.
+ */
+ mutex_lock(&local->scan_mtx);
+ swscan = local->sw_scanning;
+ mutex_unlock(&local->scan_mtx);
+
+ if (swscan)
+ ieee80211_scan_completed(&local->hw, true);
+}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 48bf78e7fa7a..68953033403d 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -84,104 +84,3 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
mgmt->sa, mgmt->bssid,
mgmt->u.action.u.measurement.dialog_token);
}
-
-void ieee80211_chswitch_work(struct work_struct *work)
-{
- struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
- struct ieee80211_bss *bss;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- if (!netif_running(sdata->dev))
- return;
-
- bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (!bss)
- goto exit;
-
- sdata->local->oper_channel = sdata->local->csa_channel;
- /* XXX: shouldn't really modify cfg80211-owned data! */
- if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
- bss->cbss.channel = sdata->local->oper_channel;
-
- ieee80211_rx_bss_put(sdata->local, bss);
-exit:
- ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CSA);
-}
-
-void ieee80211_chswitch_timer(unsigned long data)
-{
- struct ieee80211_sub_if_data *sdata =
- (struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
-}
-
-void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss)
-{
- struct ieee80211_channel *new_ch;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
-
- /* FIXME: Handle ADHOC later */
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
- return;
-
- if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
- return;
-
- if (sdata->local->sw_scanning || sdata->local->hw_scanning)
- return;
-
- /* Disregard subsequent beacons if we are already running a timer
- processing a CSA */
-
- if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
- return;
-
- new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
- if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
- return;
-
- sdata->local->csa_channel = new_ch;
-
- if (sw_elem->count <= 1) {
- queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
- } else {
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
- mod_timer(&ifmgd->chswitch_timer,
- jiffies +
- msecs_to_jiffies(sw_elem->count *
- bss->cbss.beacon_interval));
- }
-}
-
-void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- u16 capab_info, u8 *pwr_constr_elem,
- u8 pwr_constr_elem_len)
-{
- struct ieee80211_conf *conf = &sdata->local->hw.conf;
-
- if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
- return;
-
- /* Power constraint IE length should be 1 octet */
- if (pwr_constr_elem_len != 1)
- return;
-
- if ((*pwr_constr_elem <= conf->channel->max_power) &&
- (*pwr_constr_elem != sdata->local->power_constr_level)) {
- sdata->local->power_constr_level = *pwr_constr_elem;
- ieee80211_hw_config(sdata->local, 0);
- }
-}
-
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a98ea273a155..d5611d8fd0d6 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -293,6 +293,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->ps_tx_buf);
skb_queue_head_init(&sta->tx_filtered);
+ for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ sta->last_seq_ctrl[i] = cpu_to_le16(USHORT_MAX);
+
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Allocated STA %pM\n",
wiphy_name(local->hw.wiphy), sta->sta.addr);
@@ -608,6 +611,9 @@ static void sta_info_cleanup(unsigned long data)
sta_info_cleanup_expire_buffered(local, sta);
rcu_read_unlock();
+ if (local->quiescing)
+ return;
+
local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
add_timer(&local->sta_cleanup);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 164b16cbe0a5..49a1a1f76511 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -216,6 +216,7 @@ struct sta_ampdu_mlme {
* @plink_state: peer link state
* @plink_timeout: timeout of peer link
* @plink_timer: peer link watch timer
+ * @plink_timer_was_running: used by suspend/resume to restore timers
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
*/
@@ -293,6 +294,7 @@ struct sta_info {
__le16 reason;
u8 plink_retries;
bool ignore_plink_timer;
+ bool plink_timer_was_running;
enum plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8f68bf9746d0..a910148b8228 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -872,6 +872,8 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
do {
hdr = (void *) skb->data;
+ if (unlikely(ieee80211_is_pspoll(hdr->frame_control)))
+ break; /* must not overwrite AID */
next_len = skb->next ? skb->next->len : 0;
group_addr = is_multicast_ether_addr(hdr->addr1);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0689a8fbd1e6..949d857debd8 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -35,15 +35,6 @@
/* privid for wiphys to determine whether they belong to us or not */
void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-const unsigned char rfc1042_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-const unsigned char bridge_tunnel_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-
struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
{
struct ieee80211_local *local;
@@ -103,70 +94,6 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
return NULL;
}
-unsigned int ieee80211_hdrlen(__le16 fc)
-{
- unsigned int hdrlen = 24;
-
- if (ieee80211_is_data(fc)) {
- if (ieee80211_has_a4(fc))
- hdrlen = 30;
- if (ieee80211_is_data_qos(fc))
- hdrlen += IEEE80211_QOS_CTL_LEN;
- goto out;
- }
-
- if (ieee80211_is_ctl(fc)) {
- /*
- * ACK and CTS are 10 bytes, all others 16. To see how
- * to get this condition consider
- * subtype mask: 0b0000000011110000 (0x00F0)
- * ACK subtype: 0b0000000011010000 (0x00D0)
- * CTS subtype: 0b0000000011000000 (0x00C0)
- * bits that matter: ^^^ (0x00E0)
- * value of those: 0b0000000011000000 (0x00C0)
- */
- if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
- hdrlen = 10;
- else
- hdrlen = 16;
- }
-out:
- return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_hdrlen);
-
-unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
-{
- const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data;
- unsigned int hdrlen;
-
- if (unlikely(skb->len < 10))
- return 0;
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if (unlikely(hdrlen > skb->len))
- return 0;
- return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-
-int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
-{
- int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
- /* 7.1.3.5a.2 */
- switch (ae) {
- case 0:
- return 6;
- case 1:
- return 12;
- case 2:
- return 18;
- case 3:
- return 24;
- default:
- return 6;
- }
-}
-
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
@@ -1034,6 +961,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
unsigned long flags;
int res;
+ bool from_suspend = local->suspended;
+
+ /*
+ * We're going to start the hardware, at that point
+ * we are no longer suspended and can RX frames.
+ */
+ local->suspended = false;
/* restart hardware */
if (local->open_count) {
@@ -1058,6 +992,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (local->ops->sta_notify) {
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(sta, &local->sta_list, list) {
+ sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
@@ -1128,5 +1063,40 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+ /*
+ * If this is for hw restart things are still running.
+ * We may want to change that later, however.
+ */
+ if (!from_suspend)
+ return 0;
+
+#ifdef CONFIG_PM
+ local->suspended = false;
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ switch(sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ ieee80211_sta_restart(sdata);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ieee80211_ibss_restart(sdata);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ ieee80211_mesh_restart(sdata);
+ break;
+ default:
+ break;
+ }
+ }
+
+ add_timer(&local->sta_cleanup);
+
+ spin_lock_irqsave(&local->sta_lock, flags);
+ list_for_each_entry(sta, &local->sta_list, list)
+ mesh_plink_restart(sta);
+ spin_unlock_irqrestore(&local->sta_lock, flags);
+#else
+ WARN_ON(1);
+#endif
return 0;
}
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index c14394744a9c..a01154e127f0 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -37,12 +37,13 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
- if (ret)
+ if (ret && ret != -EALREADY)
return ret;
sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
- ieee80211_sta_req_auth(sdata);
+ if (ret != -EALREADY)
+ ieee80211_sta_req_auth(sdata);
return 0;
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 45b74f38b867..694343b9102b 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -23,34 +23,6 @@
*/
const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
-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 unsigned int classify_1d(struct sk_buff *skb)
-{
- unsigned int dscp;
-
- /* skb->priority values from 256->263 are magic values to
- * directly indicate a specific 802.1d priority. This is used
- * to allow 802.1d priority to be passed directly in from VLAN
- * tags, etc.
- */
- if (skb->priority >= 256 && skb->priority <= 263)
- return skb->priority - 256;
-
- switch (skb->protocol) {
- case htons(ETH_P_IP):
- dscp = ip_hdr(skb)->tos & 0xfc;
- break;
-
- default:
- return 0;
- }
-
- return dscp >> 5;
-}
-
-
static int wme_downgrade_ac(struct sk_buff *skb)
{
switch (skb->priority) {
@@ -94,7 +66,7 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb)
/* use the data classifier to determine what 802.1d tag the
* data frame has */
- skb->priority = classify_1d(skb);
+ skb->priority = cfg80211_classify8021d(skb);
/* in case we are a client verify acm is not set for this ac */
while (unlikely(local->wmm_acm & BIT(skb->priority))) {
diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c
index 0f1218b8d289..67e38a056240 100644
--- a/net/rxrpc/ar-connection.c
+++ b/net/rxrpc/ar-connection.c
@@ -343,9 +343,9 @@ static int rxrpc_connect_exclusive(struct rxrpc_sock *rx,
/* not yet present - create a candidate for a new connection
* and then redo the check */
conn = rxrpc_alloc_connection(gfp);
- if (IS_ERR(conn)) {
- _leave(" = %ld", PTR_ERR(conn));
- return PTR_ERR(conn);
+ if (!conn) {
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
}
conn->trans = trans;
@@ -508,9 +508,9 @@ int rxrpc_connect_call(struct rxrpc_sock *rx,
/* not yet present - create a candidate for a new connection and then
* redo the check */
candidate = rxrpc_alloc_connection(gfp);
- if (IS_ERR(candidate)) {
- _leave(" = %ld", PTR_ERR(candidate));
- return PTR_ERR(candidate);
+ if (!candidate) {
+ _leave(" = -ENOMEM");
+ return -ENOMEM;
}
candidate->trans = trans;
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 428a5ef5b944..a886496bdc3a 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -308,6 +308,7 @@ restart:
if (!netif_tx_queue_stopped(slave_txq) &&
!netif_tx_queue_frozen(slave_txq) &&
slave_ops->ndo_start_xmit(skb, slave) == 0) {
+ txq_trans_update(slave_txq);
__netif_tx_unlock(slave_txq);
master->slaves = NEXT_SLAVE(q);
netif_wake_queue(dev);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 3c3bc9e579ed..45005497c634 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -10,6 +10,14 @@ config CFG80211_REG_DEBUG
If unsure, say N.
+config CFG80211_DEBUGFS
+ bool "cfg80211 DebugFS entries"
+ depends on CFG80211 && DEBUG_FS
+ ---help---
+ You can enable this if you want to debugfs entries for cfg80211.
+
+ If unsure, say N.
+
config WIRELESS_OLD_REGULATORY
bool "Old wireless static regulatory definitions"
default n
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 14ea01c4a103..f78c4832a9ca 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
+cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 47c20eb0c04d..a5dbea1da476 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -17,6 +17,7 @@
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
+#include "debugfs.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@@ -228,7 +229,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
/* exported functions */
-struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
+struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
{
static int wiphy_counter;
@@ -375,6 +376,8 @@ int wiphy_register(struct wiphy *wiphy)
nl80211_send_reg_change_event(&request);
}
+ cfg80211_debugfs_drv_add(drv);
+
res = 0;
out_unlock:
mutex_unlock(&cfg80211_mutex);
@@ -405,6 +408,8 @@ void wiphy_unregister(struct wiphy *wiphy)
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
+ cfg80211_debugfs_drv_del(drv);
+
/* If this device got a regulatory hint tell core its
* free to listen now to a new shiny device regulatory hint */
reg_device_remove(wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f14b6c5f4221..ab512bcd8153 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -10,12 +10,13 @@
#include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
+#include <linux/debugfs.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "reg.h"
struct cfg80211_registered_device {
- struct cfg80211_ops *ops;
+ const struct cfg80211_ops *ops;
struct list_head list;
/* we hold this mutex during any call so that
* we cannot do multiple calls at once, and also
@@ -50,6 +51,17 @@ struct cfg80211_registered_device {
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
unsigned long suspend_at;
+#ifdef CONFIG_CFG80211_DEBUGFS
+ /* Debugfs entries */
+ struct wiphy_debugfsdentries {
+ struct dentry *rts_threshold;
+ struct dentry *fragmentation_threshold;
+ struct dentry *short_retry_limit;
+ struct dentry *long_retry_limit;
+ struct dentry *ht40allow_map;
+ } debugfs;
+#endif
+
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
new file mode 100644
index 000000000000..679ddfcec1ee
--- /dev/null
+++ b/net/wireless/debugfs.c
@@ -0,0 +1,131 @@
+/*
+ * cfg80211 debugfs
+ *
+ * Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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 "core.h"
+#include "debugfs.h"
+
+static int cfg80211_open_file_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
+static ssize_t name## _read(struct file *file, char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ struct wiphy *wiphy= file->private_data; \
+ char buf[buflen]; \
+ int res; \
+ \
+ res = scnprintf(buf, buflen, fmt "\n", ##value); \
+ return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
+} \
+ \
+static const struct file_operations name## _ops = { \
+ .read = name## _read, \
+ .open = cfg80211_open_file_generic, \
+};
+
+DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
+ wiphy->rts_threshold)
+DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
+ wiphy->frag_threshold);
+DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
+ wiphy->retry_short)
+DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
+ wiphy->retry_long);
+
+static int ht_print_chan(struct ieee80211_channel *chan,
+ char *buf, int buf_size, int offset)
+{
+ if (WARN_ON(offset > buf_size))
+ return 0;
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return snprintf(buf + offset,
+ buf_size - offset,
+ "%d Disabled\n",
+ chan->center_freq);
+
+ return snprintf(buf + offset,
+ buf_size - offset,
+ "%d HT40 %c%c\n",
+ chan->center_freq,
+ (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
+ (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+');
+}
+
+static ssize_t ht40allow_map_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wiphy *wiphy = file->private_data;
+ char *buf;
+ unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&cfg80211_mutex);
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++)
+ offset += ht_print_chan(&sband->channels[i],
+ buf, buf_size, offset);
+ }
+
+ mutex_unlock(&cfg80211_mutex);
+
+ r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+ kfree(buf);
+
+ return r;
+}
+
+static const struct file_operations ht40allow_map_ops = {
+ .read = ht40allow_map_read,
+ .open = cfg80211_open_file_generic,
+};
+
+#define DEBUGFS_ADD(name) \
+ drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \
+ &drv->wiphy, &name## _ops);
+#define DEBUGFS_DEL(name) \
+ debugfs_remove(drv->debugfs.name); \
+ drv->debugfs.name = NULL;
+
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv)
+{
+ struct dentry *phyd = drv->wiphy.debugfsdir;
+
+ DEBUGFS_ADD(rts_threshold);
+ DEBUGFS_ADD(fragmentation_threshold);
+ DEBUGFS_ADD(short_retry_limit);
+ DEBUGFS_ADD(long_retry_limit);
+ DEBUGFS_ADD(ht40allow_map);
+}
+
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv)
+{
+ DEBUGFS_DEL(rts_threshold);
+ DEBUGFS_DEL(fragmentation_threshold);
+ DEBUGFS_DEL(short_retry_limit);
+ DEBUGFS_DEL(long_retry_limit);
+ DEBUGFS_DEL(ht40allow_map);
+}
diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h
new file mode 100644
index 000000000000..c226983ae66b
--- /dev/null
+++ b/net/wireless/debugfs.h
@@ -0,0 +1,14 @@
+#ifndef __CFG80211_DEBUGFS_H
+#define __CFG80211_DEBUGFS_H
+
+#ifdef CONFIG_CFG80211_DEBUGFS
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv);
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv);
+#else
+static inline
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {}
+static inline
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {}
+#endif
+
+#endif /* __CFG80211_DEBUGFS_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a3a152f55dd0..4b4d3c8a1aed 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -57,7 +57,7 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
- .len = BUS_ID_SIZE-1 },
+ .len = 20-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
@@ -77,6 +77,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
@@ -492,7 +493,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
- u32 freq, sec_freq;
+ u32 freq;
if (!rdev->ops->set_channel) {
result = -EOPNOTSUPP;
@@ -518,33 +519,28 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
goto bad_res;
- if (channel_type == NL80211_CHAN_HT40MINUS)
- sec_freq = freq - 20;
- else if (channel_type == NL80211_CHAN_HT40PLUS)
- sec_freq = freq + 20;
- else
- sec_freq = 0;
-
- ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
-
- /* no HT capabilities */
- if (channel_type != NL80211_CHAN_NO_HT &&
- !ht_cap->ht_supported)
+ if (channel_type == NL80211_CHAN_HT40MINUS &&
+ (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
+ goto bad_res;
+ else if (channel_type == NL80211_CHAN_HT40PLUS &&
+ (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
goto bad_res;
- if (sec_freq) {
- struct ieee80211_channel *schan;
+ /*
+ * At this point we know if that if HT40 was requested
+ * we are allowed to use it and the extension channel
+ * exists.
+ */
+
+ ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
- /* no 40 MHz capabilities */
+ /* no HT capabilities or intolerant */
+ if (channel_type != NL80211_CHAN_NO_HT) {
+ if (!ht_cap->ht_supported)
+ goto bad_res;
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
(ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
goto bad_res;
-
- schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
-
- /* Secondary channel not allowed */
- if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
- goto bad_res;
}
result = rdev->ops->set_channel(&rdev->wiphy, chan,
@@ -2571,18 +2567,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rem_reg_rules) {
num_rules++;
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
- goto bad_reg;
+ return -EINVAL;
}
- if (!reg_is_valid_request(alpha2))
- return -EINVAL;
+ mutex_lock(&cfg80211_mutex);
+
+ if (!reg_is_valid_request(alpha2)) {
+ r = -EINVAL;
+ goto bad_reg;
+ }
size_of_regd = sizeof(struct ieee80211_regdomain) +
(num_rules * sizeof(struct ieee80211_reg_rule));
rd = kzalloc(size_of_regd, GFP_KERNEL);
- if (!rd)
- return -ENOMEM;
+ if (!rd) {
+ r = -ENOMEM;
+ goto bad_reg;
+ }
rd->n_reg_rules = num_rules;
rd->alpha2[0] = alpha2[0];
@@ -2599,20 +2601,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rule_idx++;
- if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
+ if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
+ r = -EINVAL;
goto bad_reg;
+ }
}
BUG_ON(rule_idx != num_rules);
- mutex_lock(&cfg80211_mutex);
r = set_regdom(rd);
+
mutex_unlock(&cfg80211_mutex);
+
return r;
bad_reg:
+ mutex_unlock(&cfg80211_mutex);
kfree(rd);
- return -EINVAL;
+ return r;
}
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 537af62ec42b..f87ac1df2df5 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -48,12 +48,6 @@ static struct regulatory_request *last_request;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
-/* Keep the ordering from large to small */
-static u32 supported_bandwidths[] = {
- MHZ_TO_KHZ(40),
- MHZ_TO_KHZ(20),
-};
-
/*
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
@@ -388,6 +382,8 @@ static int call_crda(const char *alpha2)
/* Used by nl80211 before kmalloc'ing our regulatory domain */
bool reg_is_valid_request(const char *alpha2)
{
+ assert_cfg80211_lock();
+
if (!last_request)
return false;
@@ -435,19 +431,20 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
return true;
}
-/* Returns value in KHz */
-static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
- u32 freq)
+static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
+ u32 center_freq_khz,
+ u32 bw_khz)
{
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) {
- u32 start_freq_khz = freq - supported_bandwidths[i]/2;
- u32 end_freq_khz = freq + supported_bandwidths[i]/2;
- if (start_freq_khz >= freq_range->start_freq_khz &&
- end_freq_khz <= freq_range->end_freq_khz)
- return supported_bandwidths[i];
- }
- return 0;
+ u32 start_freq_khz, end_freq_khz;
+
+ start_freq_khz = center_freq_khz - (bw_khz/2);
+ end_freq_khz = center_freq_khz + (bw_khz/2);
+
+ if (start_freq_khz >= freq_range->start_freq_khz &&
+ end_freq_khz <= freq_range->end_freq_khz)
+ return true;
+
+ return false;
}
/**
@@ -847,14 +844,17 @@ static u32 map_regdom_flags(u32 rd_flags)
static int freq_reg_info_regd(struct wiphy *wiphy,
u32 center_freq,
- u32 *bandwidth,
+ u32 desired_bw_khz,
const struct ieee80211_reg_rule **reg_rule,
const struct ieee80211_regdomain *custom_regd)
{
int i;
bool band_rule_found = false;
const struct ieee80211_regdomain *regd;
- u32 max_bandwidth = 0;
+ bool bw_fits = false;
+
+ if (!desired_bw_khz)
+ desired_bw_khz = MHZ_TO_KHZ(20);
regd = custom_regd ? custom_regd : cfg80211_regdomain;
@@ -887,38 +887,54 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
- max_bandwidth = freq_max_bandwidth(fr, center_freq);
+ bw_fits = reg_does_bw_fit(fr,
+ center_freq,
+ desired_bw_khz);
- if (max_bandwidth && *bandwidth <= max_bandwidth) {
+ if (band_rule_found && bw_fits) {
*reg_rule = rr;
- *bandwidth = max_bandwidth;
- break;
+ return 0;
}
}
if (!band_rule_found)
return -ERANGE;
- return !max_bandwidth;
+ return -EINVAL;
}
EXPORT_SYMBOL(freq_reg_info);
-int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
- const struct ieee80211_reg_rule **reg_rule)
+int freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 desired_bw_khz,
+ const struct ieee80211_reg_rule **reg_rule)
{
assert_cfg80211_lock();
- return freq_reg_info_regd(wiphy, center_freq,
- bandwidth, reg_rule, NULL);
+ return freq_reg_info_regd(wiphy,
+ center_freq,
+ desired_bw_khz,
+ reg_rule,
+ NULL);
}
+/*
+ * Note that right now we assume the desired channel bandwidth
+ * is always 20 MHz for each individual channel (HT40 uses 20 MHz
+ * per channel, the primary and the extension channel). To support
+ * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
+ * new ieee80211_channel.target_bw and re run the regulatory check
+ * on the wiphy with the target_bw specified. Then we can simply use
+ * that below for the desired_bw_khz below.
+ */
static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
unsigned int chan_idx)
{
int r;
- u32 flags;
- u32 max_bandwidth = 0;
+ u32 flags, bw_flags = 0;
+ u32 desired_bw_khz = MHZ_TO_KHZ(20);
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
+ const struct ieee80211_freq_range *freq_range = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct wiphy *request_wiphy = NULL;
@@ -933,8 +949,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
flags = chan->orig_flags;
- r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
- &max_bandwidth, &reg_rule);
+ r = freq_reg_info(wiphy,
+ MHZ_TO_KHZ(chan->center_freq),
+ desired_bw_khz,
+ &reg_rule);
if (r) {
/*
@@ -977,6 +995,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
}
power_rule = &reg_rule->power_rule;
+ freq_range = &reg_rule->freq_range;
+
+ if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags = IEEE80211_CHAN_NO_HT40;
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@@ -987,19 +1009,19 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
* settings
*/
chan->flags = chan->orig_flags =
- map_regdom_flags(reg_rule->flags);
+ map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = chan->orig_mag =
(int) MBI_TO_DBI(power_rule->max_antenna_gain);
- chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+ chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
chan->max_power = chan->orig_mpwr =
(int) MBM_TO_DBM(power_rule->max_eirp);
return;
}
- chan->flags = flags | map_regdom_flags(reg_rule->flags);
+ chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain = min(chan->orig_mag,
(int) MBI_TO_DBI(power_rule->max_antenna_gain));
- chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+ chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
if (chan->orig_mpwr)
chan->max_power = min(chan->orig_mpwr,
(int) MBM_TO_DBM(power_rule->max_eirp));
@@ -1156,6 +1178,93 @@ static void reg_process_beacons(struct wiphy *wiphy)
wiphy_update_beacon_reg(wiphy);
}
+static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
+{
+ if (!chan)
+ return true;
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return true;
+ /* This would happen when regulatory rules disallow HT40 completely */
+ if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
+ return true;
+ return false;
+}
+
+static void reg_process_ht_flags_channel(struct wiphy *wiphy,
+ enum ieee80211_band band,
+ unsigned int chan_idx)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *channel;
+ struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
+ unsigned int i;
+
+ assert_cfg80211_lock();
+
+ sband = wiphy->bands[band];
+ BUG_ON(chan_idx >= sband->n_channels);
+ channel = &sband->channels[chan_idx];
+
+ if (is_ht40_not_allowed(channel)) {
+ channel->flags |= IEEE80211_CHAN_NO_HT40;
+ return;
+ }
+
+ /*
+ * We need to ensure the extension channels exist to
+ * be able to use HT40- or HT40+, this finds them (or not)
+ */
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *c = &sband->channels[i];
+ if (c->center_freq == (channel->center_freq - 20))
+ channel_before = c;
+ if (c->center_freq == (channel->center_freq + 20))
+ channel_after = c;
+ }
+
+ /*
+ * Please note that this assumes target bandwidth is 20 MHz,
+ * if that ever changes we also need to change the below logic
+ * to include that as well.
+ */
+ if (is_ht40_not_allowed(channel_before))
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+
+ if (is_ht40_not_allowed(channel_after))
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ else
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+}
+
+static void reg_process_ht_flags_band(struct wiphy *wiphy,
+ enum ieee80211_band band)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ BUG_ON(!wiphy->bands[band]);
+ sband = wiphy->bands[band];
+
+ for (i = 0; i < sband->n_channels; i++)
+ reg_process_ht_flags_channel(wiphy, band, i);
+}
+
+static void reg_process_ht_flags(struct wiphy *wiphy)
+{
+ enum ieee80211_band band;
+
+ if (!wiphy)
+ return;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (wiphy->bands[band])
+ reg_process_ht_flags_band(wiphy, band);
+ }
+
+}
+
void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
@@ -1169,6 +1278,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy,
}
out:
reg_process_beacons(wiphy);
+ reg_process_ht_flags(wiphy);
if (wiphy->reg_notifier)
wiphy->reg_notifier(wiphy, last_request);
}
@@ -1179,9 +1289,11 @@ static void handle_channel_custom(struct wiphy *wiphy,
const struct ieee80211_regdomain *regd)
{
int r;
- u32 max_bandwidth = 0;
+ u32 desired_bw_khz = MHZ_TO_KHZ(20);
+ u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
+ const struct ieee80211_freq_range *freq_range = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
@@ -1191,8 +1303,11 @@ static void handle_channel_custom(struct wiphy *wiphy,
BUG_ON(chan_idx >= sband->n_channels);
chan = &sband->channels[chan_idx];
- r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
- &max_bandwidth, &reg_rule, regd);
+ r = freq_reg_info_regd(wiphy,
+ MHZ_TO_KHZ(chan->center_freq),
+ desired_bw_khz,
+ &reg_rule,
+ regd);
if (r) {
chan->flags = IEEE80211_CHAN_DISABLED;
@@ -1200,10 +1315,14 @@ static void handle_channel_custom(struct wiphy *wiphy,
}
power_rule = &reg_rule->power_rule;
+ freq_range = &reg_rule->freq_range;
+
+ if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags = IEEE80211_CHAN_NO_HT40;
- chan->flags |= map_regdom_flags(reg_rule->flags);
+ chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
- chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+ chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
}
@@ -1225,13 +1344,22 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
const struct ieee80211_regdomain *regd)
{
enum ieee80211_band band;
+ unsigned int bands_set = 0;
mutex_lock(&cfg80211_mutex);
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (wiphy->bands[band])
- handle_band_custom(wiphy, band, regd);
+ if (!wiphy->bands[band])
+ continue;
+ handle_band_custom(wiphy, band, regd);
+ bands_set++;
}
mutex_unlock(&cfg80211_mutex);
+
+ /*
+ * no point in calling this if it won't have any effect
+ * on your device's supportd bands.
+ */
+ WARN_ON(!bands_set);
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
@@ -1552,6 +1680,13 @@ static int regulatory_hint_core(const char *alpha2)
queue_regulatory_request(request);
+ /*
+ * This ensures last_request is populated once modules
+ * come swinging in and calling regulatory hints and
+ * wiphy_apply_custom_regulatory().
+ */
+ flush_scheduled_work();
+
return 0;
}
diff --git a/net/wireless/util.c b/net/wireless/util.c
index beb226e78cd7..d072bff463aa 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -4,7 +4,9 @@
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/bitops.h>
+#include <linux/etherdevice.h>
#include <net/cfg80211.h>
+#include <net/ip.h>
#include "core.h"
struct ieee80211_rate *
@@ -181,5 +183,323 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
return -EINVAL;
}
+ if (params->seq) {
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* These ciphers do not use key sequence */
+ return -EINVAL;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (params->seq_len != 6)
+ return -EINVAL;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+EXPORT_SYMBOL(rfc1042_header);
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+EXPORT_SYMBOL(bridge_tunnel_header);
+
+unsigned int ieee80211_hdrlen(__le16 fc)
+{
+ unsigned int hdrlen = 24;
+
+ if (ieee80211_is_data(fc)) {
+ if (ieee80211_has_a4(fc))
+ hdrlen = 30;
+ if (ieee80211_is_data_qos(fc))
+ hdrlen += IEEE80211_QOS_CTL_LEN;
+ goto out;
+ }
+
+ if (ieee80211_is_ctl(fc)) {
+ /*
+ * ACK and CTS are 10 bytes, all others 16. To see how
+ * to get this condition consider
+ * subtype mask: 0b0000000011110000 (0x00F0)
+ * ACK subtype: 0b0000000011010000 (0x00D0)
+ * CTS subtype: 0b0000000011000000 (0x00C0)
+ * bits that matter: ^^^ (0x00E0)
+ * value of those: 0b0000000011000000 (0x00C0)
+ */
+ if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
+ hdrlen = 10;
+ else
+ hdrlen = 16;
+ }
+out:
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_hdrlen);
+
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+{
+ const struct ieee80211_hdr *hdr =
+ (const struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
+
+ if (unlikely(skb->len < 10))
+ return 0;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (unlikely(hdrlen > skb->len))
+ return 0;
+ return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+ int ae = meshhdr->flags & MESH_FLAGS_AE;
+ /* 7.1.3.5a.2 */
+ switch (ae) {
+ case 0:
+ return 6;
+ case 1:
+ return 12;
+ case 2:
+ return 18;
+ case 3:
+ return 24;
+ default:
+ return 6;
+ }
+}
+
+int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
+ enum nl80211_iftype iftype)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u16 hdrlen, ethertype;
+ u8 *payload;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN] __aligned(2);
+
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+ return -1;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ /* convert IEEE 802.11 header + possible LLC headers into Ethernet
+ * header
+ * IEEE 802.11 address fields:
+ * ToDS FromDS Addr1 Addr2 Addr3 Addr4
+ * 0 0 DA SA BSSID n/a
+ * 0 1 DA BSSID SA n/a
+ * 1 0 BSSID SA DA n/a
+ * 1 1 RA TA DA SA
+ */
+ memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
+ memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
+
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case cpu_to_le16(IEEE80211_FCTL_TODS):
+ if (unlikely(iftype != NL80211_IFTYPE_AP &&
+ iftype != NL80211_IFTYPE_AP_VLAN))
+ return -1;
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ if (unlikely(iftype != NL80211_IFTYPE_WDS &&
+ iftype != NL80211_IFTYPE_MESH_POINT))
+ return -1;
+ if (iftype == NL80211_IFTYPE_MESH_POINT) {
+ struct ieee80211s_hdr *meshdr =
+ (struct ieee80211s_hdr *) (skb->data + hdrlen);
+ hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+ if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
+ memcpy(dst, meshdr->eaddr1, ETH_ALEN);
+ memcpy(src, meshdr->eaddr2, ETH_ALEN);
+ }
+ }
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ if (iftype != NL80211_IFTYPE_STATION ||
+ (is_multicast_ether_addr(dst) &&
+ !compare_ether_addr(src, addr)))
+ return -1;
+ break;
+ case cpu_to_le16(0):
+ if (iftype != NL80211_IFTYPE_ADHOC)
+ return -1;
+ break;
+ }
+
+ if (unlikely(skb->len - hdrlen < 8))
+ return -1;
+
+ payload = skb->data + hdrlen;
+ ethertype = (payload[6] << 8) | payload[7];
+
+ if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(skb, hdrlen + 6);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ struct ethhdr *ehdr;
+ __be16 len;
+
+ skb_pull(skb, hdrlen);
+ len = htons(skb->len);
+ ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
+ memcpy(ehdr->h_dest, dst, ETH_ALEN);
+ memcpy(ehdr->h_source, src, ETH_ALEN);
+ ehdr->h_proto = len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_data_to_8023);
+
+int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
+ enum nl80211_iftype iftype, u8 *bssid, bool qos)
+{
+ struct ieee80211_hdr hdr;
+ u16 hdrlen, ethertype;
+ __le16 fc;
+ const u8 *encaps_data;
+ int encaps_len, skip_header_bytes;
+ int nh_pos, h_pos;
+ int head_need;
+
+ if (unlikely(skb->len < ETH_HLEN))
+ return -EINVAL;
+
+ nh_pos = skb_network_header(skb) - skb->data;
+ h_pos = skb_transport_header(skb) - skb->data;
+
+ /* convert Ethernet header to proper 802.11 header (based on
+ * operation mode) */
+ ethertype = (skb->data[12] << 8) | skb->data[13];
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+
+ switch (iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ /* DA BSSID SA */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case NL80211_IFTYPE_STATION:
+ fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
+ /* BSSID SA DA */
+ memcpy(hdr.addr1, bssid, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, bssid, ETH_ALEN);
+ hdrlen = 24;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (qos) {
+ fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+ hdrlen += 2;
+ }
+
+ hdr.frame_control = fc;
+ hdr.duration_id = 0;
+ hdr.seq_ctrl = 0;
+
+ skip_header_bytes = ETH_HLEN;
+ if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+ encaps_data = bridge_tunnel_header;
+ encaps_len = sizeof(bridge_tunnel_header);
+ skip_header_bytes -= 2;
+ } else if (ethertype > 0x600) {
+ encaps_data = rfc1042_header;
+ encaps_len = sizeof(rfc1042_header);
+ skip_header_bytes -= 2;
+ } else {
+ encaps_data = NULL;
+ encaps_len = 0;
+ }
+
+ skb_pull(skb, skip_header_bytes);
+ nh_pos -= skip_header_bytes;
+ h_pos -= skip_header_bytes;
+
+ head_need = hdrlen + encaps_len - skb_headroom(skb);
+
+ if (head_need > 0 || skb_cloned(skb)) {
+ head_need = max(head_need, 0);
+ if (head_need)
+ skb_orphan(skb);
+
+ if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
+ printk(KERN_ERR "failed to reallocate Tx buffer\n");
+ return -ENOMEM;
+ }
+ skb->truesize += head_need;
+ }
+
+ if (encaps_data) {
+ memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+ nh_pos += encaps_len;
+ h_pos += encaps_len;
+ }
+
+ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
+
+ nh_pos += hdrlen;
+ h_pos += hdrlen;
+
+ /* Update skb pointers to various headers since this modified frame
+ * is going to go through Linux networking code that may potentially
+ * need things like pointer to IP header. */
+ skb_set_mac_header(skb, 0);
+ skb_set_network_header(skb, nh_pos);
+ skb_set_transport_header(skb, h_pos);
+
return 0;
}
+EXPORT_SYMBOL(ieee80211_data_from_8023);
+
+/* Given a data frame determine the 802.1p/1d tag to use. */
+unsigned int cfg80211_classify8021d(struct sk_buff *skb)
+{
+ unsigned int dscp;
+
+ /* skb->priority values from 256->263 are magic values to
+ * directly indicate a specific 802.1d priority. This is used
+ * to allow 802.1d priority to be passed directly in from VLAN
+ * tags, etc.
+ */
+ if (skb->priority >= 256 && skb->priority <= 263)
+ return skb->priority - 256;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ip_hdr(skb)->tos & 0xfc;
+ break;
+ default:
+ return 0;
+ }
+
+ return dscp >> 5;
+}
+EXPORT_SYMBOL(cfg80211_classify8021d);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index f98090b90fbf..711e00a0c9b5 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -504,6 +504,13 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
else if (idx == wdev->wext.default_mgmt_key)
wdev->wext.default_mgmt_key = -1;
}
+ /*
+ * Applications using wireless extensions expect to be
+ * able to delete keys that don't exist, so allow that.
+ */
+ if (err == -ENOENT)
+ return 0;
+
return err;
} else {
if (addr)
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d3bbef70cc7c..252c2010c2e2 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -636,8 +636,10 @@ static void wireless_seq_printf_stats(struct seq_file *seq,
/*
* Print info for /proc/net/wireless (print all entries)
*/
-static int wireless_seq_show(struct seq_file *seq, void *v)
+static int wireless_dev_seq_show(struct seq_file *seq, void *v)
{
+ might_sleep();
+
if (v == SEQ_START_TOKEN)
seq_printf(seq, "Inter-| sta-| Quality | Discarded "
"packets | Missed | WE\n"
@@ -651,21 +653,41 @@ static int wireless_seq_show(struct seq_file *seq, void *v)
static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
{
+ struct net *net = seq_file_net(seq);
+ loff_t off;
+ struct net_device *dev;
+
rtnl_lock();
- return dev_seq_start(seq, pos);
+ if (!*pos)
+ return SEQ_START_TOKEN;
+
+ off = 1;
+ for_each_netdev(net, dev)
+ if (off++ == *pos)
+ return dev;
+ return NULL;
+}
+
+static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct net *net = seq_file_net(seq);
+
+ ++*pos;
+
+ return v == SEQ_START_TOKEN ?
+ first_net_device(net) : next_net_device(v);
}
static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
{
- dev_seq_stop(seq, v);
rtnl_unlock();
}
static const struct seq_operations wireless_seq_ops = {
.start = wireless_dev_seq_start,
- .next = dev_seq_next,
+ .next = wireless_dev_seq_next,
.stop = wireless_dev_seq_stop,
- .show = wireless_seq_show,
+ .show = wireless_dev_seq_show,
};
static int seq_open_wireless(struct inode *inode, struct file *file)
@@ -798,6 +820,13 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
err = -EFAULT;
goto out;
}
+
+ if (cmd == SIOCSIWENCODEEXT) {
+ struct iw_encode_ext *ee = (void *) extra;
+
+ if (iwp->length < sizeof(*ee) + ee->key_len)
+ return -EFAULT;
+ }
}
err = handler(dev, info, (union iwreq_data *) iwp, extra);