From 1a6afe8a733a3edaa1816c10ec2a7353ae0ff47b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:00 +0000 Subject: clip: convert to internal network_device_stats Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/atmclip.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/net') diff --git a/include/net/atmclip.h b/include/net/atmclip.h index b5a51a7bb36..467c531b8a7 100644 --- a/include/net/atmclip.h +++ b/include/net/atmclip.h @@ -50,7 +50,6 @@ struct atmarp_entry { struct clip_priv { int number; /* for convenience ... */ spinlock_t xoff_lock; /* ensures that pop is atomic (SMP) */ - struct net_device_stats stats; struct net_device *next; /* next CLIP interface */ }; -- cgit v1.2.3-70-g09d2 From b51414b69148433a79af5dc93463a0489492a788 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:03 +0000 Subject: netrom: convert to internal net_device_stats Signed-off-by: Stephen Hemminger Acked-by: Ralf Baechle Signed-off-by: David S. Miller --- include/net/netrom.h | 4 ---- net/netrom/af_netrom.c | 2 +- net/netrom/nr_dev.c | 14 ++------------ 3 files changed, 3 insertions(+), 17 deletions(-) (limited to 'include/net') diff --git a/include/net/netrom.h b/include/net/netrom.h index f06852bba62..15696b1fd30 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -59,10 +59,6 @@ enum { #define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ #define NR_MAX_PACKET_SIZE 236 /* Maximum Packet Length - 236 */ -struct nr_private { - struct net_device_stats stats; -}; - struct nr_sock { struct sock sock; ax25_address user_addr, source_addr, dest_addr; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index e9c05b8f4f4..cba7849de98 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1432,7 +1432,7 @@ static int __init nr_proto_init(void) struct net_device *dev; sprintf(name, "nr%d", i); - dev = alloc_netdev(sizeof(struct nr_private), name, nr_setup); + dev = alloc_netdev(0, name, nr_setup); if (!dev) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n"); goto fail; diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 6caf459665f..5b9a31a6e68 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -42,7 +42,7 @@ int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) { - struct net_device_stats *stats = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; if (!netif_running(dev)) { stats->rx_dropped++; @@ -171,8 +171,7 @@ static int nr_close(struct net_device *dev) static int nr_xmit(struct sk_buff *skb, struct net_device *dev) { - struct nr_private *nr = netdev_priv(dev); - struct net_device_stats *stats = &nr->stats; + struct net_device_stats *stats = &dev->stats; unsigned int len = skb->len; if (!nr_route_frame(skb, NULL)) { @@ -187,13 +186,6 @@ static int nr_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static struct net_device_stats *nr_get_stats(struct net_device *dev) -{ - struct nr_private *nr = netdev_priv(dev); - - return &nr->stats; -} - static const struct header_ops nr_header_ops = { .create = nr_header, .rebuild= nr_rebuild_header, @@ -215,6 +207,4 @@ void nr_setup(struct net_device *dev) /* New-style flags. */ dev->flags = IFF_NOARP; - - dev->get_stats = nr_get_stats; } -- cgit v1.2.3-70-g09d2 From a9d8f9110d7e953c2f2b521087a4179677843c2a Mon Sep 17 00:00:00 2001 From: Evgeniy Polyakov Date: Mon, 19 Jan 2009 16:46:02 -0800 Subject: inet: Allowing more than 64k connections and heavily optimize bind(0) time. With simple extension to the binding mechanism, which allows to bind more than 64k sockets (or smaller amount, depending on sysctl parameters), we have to traverse the whole bind hash table to find out empty bucket. And while it is not a problem for example for 32k connections, bind() completion time grows exponentially (since after each successful binding we have to traverse one bucket more to find empty one) even if we start each time from random offset inside the hash table. So, when hash table is full, and we want to add another socket, we have to traverse the whole table no matter what, so effectivelly this will be the worst case performance and it will be constant. Attached picture shows bind() time depending on number of already bound sockets. Green area corresponds to the usual binding to zero port process, which turns on kernel port selection as described above. Red area is the bind process, when number of reuse-bound sockets is not limited by 64k (or sysctl parameters). The same exponential growth (hidden by the green area) before number of ports reaches sysctl limit. At this time bind hash table has exactly one reuse-enbaled socket in a bucket, but it is possible that they have different addresses. Actually kernel selects the first port to try randomly, so at the beginning bind will take roughly constant time, but with time number of port to check after random start will increase. And that will have exponential growth, but because of above random selection, not every next port selection will necessary take longer time than previous. So we have to consider the area below in the graph (if you could zoom it, you could find, that there are many different times placed there), so area can hide another. Blue area corresponds to the port selection optimization. This is rather simple design approach: hashtable now maintains (unprecise and racely updated) number of currently bound sockets, and when number of such sockets becomes greater than predefined value (I use maximum port range defined by sysctls), we stop traversing the whole bind hash table and just stop at first matching bucket after random start. Above limit roughly corresponds to the case, when bind hash table is full and we turned on mechanism of allowing to bind more reuse-enabled sockets, so it does not change behaviour of other sockets. Signed-off-by: Evgeniy Polyakov Tested-by: Denys Fedoryschenko Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 3 ++- net/ipv4/inet_connection_sock.c | 41 ++++++++++++++++++++++++++++++++++------- net/ipv4/inet_hashtables.c | 11 ++++++++++- 3 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index f44bb5c77a7..cdc08c19063 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -82,6 +82,7 @@ struct inet_bind_bucket { #endif unsigned short port; signed short fastreuse; + int num_owners; struct hlist_node node; struct hlist_head owners; }; @@ -133,7 +134,7 @@ struct inet_hashinfo { struct inet_bind_hashbucket *bhash; unsigned int bhash_size; - /* Note : 4 bytes padding on 64 bit arches */ + int bsockets; struct kmem_cache *bind_bucket_cachep; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index f26ab38680d..df8e72f0747 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -93,24 +93,40 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) struct inet_bind_hashbucket *head; struct hlist_node *node; struct inet_bind_bucket *tb; - int ret; + int ret, attempts = 5; struct net *net = sock_net(sk); + int smallest_size = -1, smallest_rover; local_bh_disable(); if (!snum) { int remaining, rover, low, high; +again: inet_get_local_port_range(&low, &high); remaining = (high - low) + 1; - rover = net_random() % remaining + low; + smallest_rover = rover = net_random() % remaining + low; + smallest_size = -1; do { head = &hashinfo->bhash[inet_bhashfn(net, rover, hashinfo->bhash_size)]; spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) - if (ib_net(tb) == net && tb->port == rover) + if (ib_net(tb) == net && tb->port == rover) { + if (tb->fastreuse > 0 && + sk->sk_reuse && + sk->sk_state != TCP_LISTEN && + (tb->num_owners < smallest_size || smallest_size == -1)) { + smallest_size = tb->num_owners; + smallest_rover = rover; + if (hashinfo->bsockets > (high - low) + 1) { + spin_unlock(&head->lock); + snum = smallest_rover; + goto have_snum; + } + } goto next; + } break; next: spin_unlock(&head->lock); @@ -125,14 +141,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) * the top level, not from the 'break;' statement. */ ret = 1; - if (remaining <= 0) + if (remaining <= 0) { + if (smallest_size != -1) { + snum = smallest_rover; + goto have_snum; + } goto fail; - + } /* OK, here is the one we will use. HEAD is * non-NULL and we hold it's mutex. */ snum = rover; } else { +have_snum: head = &hashinfo->bhash[inet_bhashfn(net, snum, hashinfo->bhash_size)]; spin_lock(&head->lock); @@ -145,12 +166,18 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) tb_found: if (!hlist_empty(&tb->owners)) { if (tb->fastreuse > 0 && - sk->sk_reuse && sk->sk_state != TCP_LISTEN) { + sk->sk_reuse && sk->sk_state != TCP_LISTEN && + smallest_size == -1) { goto success; } else { ret = 1; - if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) + if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) { + if (sk->sk_reuse && sk->sk_state != TCP_LISTEN && --attempts >= 0) { + spin_unlock(&head->lock); + goto again; + } goto fail_unlock; + } } } tb_not_found: diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 6a1045da48d..d7b6178bf48 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -38,6 +38,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep, write_pnet(&tb->ib_net, hold_net(net)); tb->port = snum; tb->fastreuse = 0; + tb->num_owners = 0; INIT_HLIST_HEAD(&tb->owners); hlist_add_head(&tb->node, &head->chain); } @@ -59,8 +60,13 @@ void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, const unsigned short snum) { + struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; + + hashinfo->bsockets++; + inet_sk(sk)->num = snum; sk_add_bind_node(sk, &tb->owners); + tb->num_owners++; inet_csk(sk)->icsk_bind_hash = tb; } @@ -75,9 +81,12 @@ static void __inet_put_port(struct sock *sk) struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash]; struct inet_bind_bucket *tb; + hashinfo->bsockets--; + spin_lock(&head->lock); tb = inet_csk(sk)->icsk_bind_hash; __sk_del_bind_node(sk); + tb->num_owners--; inet_csk(sk)->icsk_bind_hash = NULL; inet_sk(sk)->num = 0; inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); @@ -444,9 +453,9 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, */ inet_bind_bucket_for_each(tb, node, &head->chain) { if (ib_net(tb) == net && tb->port == port) { - WARN_ON(hlist_empty(&tb->owners)); if (tb->fastreuse >= 0) goto next_port; + WARN_ON(hlist_empty(&tb->owners)); if (!check_established(death_row, sk, port, &tw)) goto ok; -- cgit v1.2.3-70-g09d2 From 70a269e6c9c9b38b1a37dce068c59e9a912f8578 Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:15 +0000 Subject: netns: ipmr: allocate mroute_socket per-namespace. Preliminary work to make IPv4 multicast routing netns-aware. Make IPv4 multicast routing mroute_socket per-namespace, moves it into struct netns_ipv4. At the moment, mroute_socket is only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 4 ++++ net/ipv4/ipmr.c | 28 +++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 977f482d97a..4f00722c747 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -54,5 +54,9 @@ struct netns_ipv4 { struct timer_list rt_secret_timer; atomic_t rt_genid; + +#ifdef CONFIG_IP_MROUTE + struct sock *mroute_sk; +#endif }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 14666449dc1..ac324b702e8 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -67,9 +67,6 @@ #define CONFIG_IP_PIMSM 1 #endif -static struct sock *mroute_socket; - - /* Big lock, protecting vif table, mrt cache and mroute socket state. Note that the changes are semaphored via rtnl_lock. */ @@ -658,7 +655,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) skb->transport_header = skb->network_header; } - if (mroute_socket == NULL) { + if (init_net.ipv4.mroute_sk == NULL) { kfree_skb(skb); return -EINVAL; } @@ -666,7 +663,8 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) /* * Deliver to mrouted */ - if ((ret = sock_queue_rcv_skb(mroute_socket, skb))<0) { + ret = sock_queue_rcv_skb(init_net.ipv4.mroute_sk, skb); + if (ret < 0) { if (net_ratelimit()) printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n"); kfree_skb(skb); @@ -896,11 +894,11 @@ static void mroute_clean_tables(struct sock *sk) static void mrtsock_destruct(struct sock *sk) { rtnl_lock(); - if (sk == mroute_socket) { + if (sk == init_net.ipv4.mroute_sk) { IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)--; write_lock_bh(&mrt_lock); - mroute_socket = NULL; + init_net.ipv4.mroute_sk = NULL; write_unlock_bh(&mrt_lock); mroute_clean_tables(sk); @@ -922,7 +920,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int struct mfcctl mfc; if (optname != MRT_INIT) { - if (sk != mroute_socket && !capable(CAP_NET_ADMIN)) + if (sk != init_net.ipv4.mroute_sk && !capable(CAP_NET_ADMIN)) return -EACCES; } @@ -935,7 +933,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int return -ENOPROTOOPT; rtnl_lock(); - if (mroute_socket) { + if (init_net.ipv4.mroute_sk) { rtnl_unlock(); return -EADDRINUSE; } @@ -943,7 +941,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int ret = ip_ra_control(sk, 1, mrtsock_destruct); if (ret == 0) { write_lock_bh(&mrt_lock); - mroute_socket = sk; + init_net.ipv4.mroute_sk = sk; write_unlock_bh(&mrt_lock); IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)++; @@ -951,7 +949,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int rtnl_unlock(); return ret; case MRT_DONE: - if (sk != mroute_socket) + if (sk != init_net.ipv4.mroute_sk) return -EACCES; return ip_ra_control(sk, 0, NULL); case MRT_ADD_VIF: @@ -964,7 +962,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int return -ENFILE; rtnl_lock(); if (optname == MRT_ADD_VIF) { - ret = vif_add(&vif, sk==mroute_socket); + ret = vif_add(&vif, sk == init_net.ipv4.mroute_sk); } else { ret = vif_delete(vif.vifc_vifi, 0); } @@ -985,7 +983,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int if (optname == MRT_DEL_MFC) ret = ipmr_mfc_delete(&mfc); else - ret = ipmr_mfc_add(&mfc, sk==mroute_socket); + ret = ipmr_mfc_add(&mfc, sk == init_net.ipv4.mroute_sk); rtnl_unlock(); return ret; /* @@ -1425,9 +1423,9 @@ int ip_mr_input(struct sk_buff *skb) that we can forward NO IGMP messages. */ read_lock(&mrt_lock); - if (mroute_socket) { + if (init_net.ipv4.mroute_sk) { nf_reset(skb); - raw_rcv(mroute_socket, skb); + raw_rcv(init_net.ipv4.mroute_sk, skb); read_unlock(&mrt_lock); return 0; } -- cgit v1.2.3-70-g09d2 From cf958ae377ee2545ae70cf48d38e7eb4308c27ea Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:16 +0000 Subject: netns: ipmr: dynamically allocate vif_table Preliminary work to make IPv6 multicast routing netns-aware. Dynamically allocate interface table vif_table and move it to struct netns_ipv4, and update MIF_EXISTS() macro. At the moment, vif_table is only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 + net/ipv4/ipmr.c | 109 +++++++++++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 41 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 4f00722c747..edbcccbbc31 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -57,6 +57,8 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MROUTE struct sock *mroute_sk; + struct vif_device *vif_table; + int maxvif; #endif }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index ac324b702e8..75a5f79cc22 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -77,10 +77,7 @@ static DEFINE_RWLOCK(mrt_lock); * Multicast router control variables */ -static struct vif_device vif_table[MAXVIFS]; /* Devices */ -static int maxvif; - -#define VIF_EXISTS(idx) (vif_table[idx].dev != NULL) +#define VIF_EXISTS(_net, _idx) ((_net)->ipv4.vif_table[_idx].dev != NULL) static int mroute_do_assert; /* Set in PIM assert */ static int mroute_do_pim; @@ -286,10 +283,10 @@ static int vif_delete(int vifi, int notify) struct net_device *dev; struct in_device *in_dev; - if (vifi < 0 || vifi >= maxvif) + if (vifi < 0 || vifi >= init_net.ipv4.maxvif) return -EADDRNOTAVAIL; - v = &vif_table[vifi]; + v = &init_net.ipv4.vif_table[vifi]; write_lock_bh(&mrt_lock); dev = v->dev; @@ -305,13 +302,13 @@ static int vif_delete(int vifi, int notify) reg_vif_num = -1; #endif - if (vifi+1 == maxvif) { + if (vifi+1 == init_net.ipv4.maxvif) { int tmp; for (tmp=vifi-1; tmp>=0; tmp--) { - if (VIF_EXISTS(tmp)) + if (VIF_EXISTS(&init_net, tmp)) break; } - maxvif = tmp+1; + init_net.ipv4.maxvif = tmp+1; } write_unlock_bh(&mrt_lock); @@ -411,8 +408,9 @@ static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls) cache->mfc_un.res.maxvif = 0; memset(cache->mfc_un.res.ttls, 255, MAXVIFS); - for (vifi=0; vifimfc_un.res.ttls[vifi] = ttls[vifi]; if (cache->mfc_un.res.minvif > vifi) cache->mfc_un.res.minvif = vifi; @@ -425,13 +423,13 @@ static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls) static int vif_add(struct vifctl *vifc, int mrtsock) { int vifi = vifc->vifc_vifi; - struct vif_device *v = &vif_table[vifi]; + struct vif_device *v = &init_net.ipv4.vif_table[vifi]; struct net_device *dev; struct in_device *in_dev; int err; /* Is vif busy ? */ - if (VIF_EXISTS(vifi)) + if (VIF_EXISTS(&init_net, vifi)) return -EADDRINUSE; switch (vifc->vifc_flags) { @@ -509,8 +507,8 @@ static int vif_add(struct vifctl *vifc, int mrtsock) if (v->flags&VIFF_REGISTER) reg_vif_num = vifi; #endif - if (vifi+1 > maxvif) - maxvif = vifi+1; + if (vifi+1 > init_net.ipv4.maxvif) + init_net.ipv4.maxvif = vifi+1; write_unlock_bh(&mrt_lock); return 0; } @@ -849,8 +847,8 @@ static void mroute_clean_tables(struct sock *sk) /* * Shut down all active vif entries */ - for (i=0; i= maxvif) + if (vr.vifi >= init_net.ipv4.maxvif) return -EINVAL; read_lock(&mrt_lock); - vif=&vif_table[vr.vifi]; - if (VIF_EXISTS(vr.vifi)) { + vif = &init_net.ipv4.vif_table[vr.vifi]; + if (VIF_EXISTS(&init_net, vr.vifi)) { vr.icount = vif->pkt_in; vr.ocount = vif->pkt_out; vr.ibytes = vif->bytes_in; @@ -1140,8 +1138,8 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; - v=&vif_table[0]; - for (ct=0; ctdev == dev) vif_delete(ct, 1); } @@ -1204,7 +1202,7 @@ static inline int ipmr_forward_finish(struct sk_buff *skb) static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) { const struct iphdr *iph = ip_hdr(skb); - struct vif_device *vif = &vif_table[vifi]; + struct vif_device *vif = &init_net.ipv4.vif_table[vifi]; struct net_device *dev; struct rtable *rt; int encap = 0; @@ -1305,8 +1303,8 @@ out_free: static int ipmr_find_vif(struct net_device *dev) { int ct; - for (ct=maxvif-1; ct>=0; ct--) { - if (vif_table[ct].dev == dev) + for (ct = init_net.ipv4.maxvif-1; ct >= 0; ct--) { + if (init_net.ipv4.vif_table[ct].dev == dev) break; } return ct; @@ -1326,7 +1324,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local /* * Wrong interface: drop packet and (maybe) send PIM assert. */ - if (vif_table[vif].dev != skb->dev) { + if (init_net.ipv4.vif_table[vif].dev != skb->dev) { int true_vifi; if (skb->rtable->fl.iif == 0) { @@ -1362,8 +1360,8 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local goto dont_forward; } - vif_table[vif].pkt_in++; - vif_table[vif].bytes_in += skb->len; + init_net.ipv4.vif_table[vif].pkt_in++; + init_net.ipv4.vif_table[vif].bytes_in += skb->len; /* * Forward the frame @@ -1500,7 +1498,7 @@ static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) read_lock(&mrt_lock); if (reg_vif_num >= 0) - reg_dev = vif_table[reg_vif_num].dev; + reg_dev = init_net.ipv4.vif_table[reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); @@ -1581,7 +1579,7 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) { int ct; struct rtnexthop *nhp; - struct net_device *dev = vif_table[c->mfc_parent].dev; + struct net_device *dev = init_net.ipv4.vif_table[c->mfc_parent].dev; u8 *b = skb_tail_pointer(skb); struct rtattr *mp_head; @@ -1597,7 +1595,7 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); nhp->rtnh_flags = 0; nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; - nhp->rtnh_ifindex = vif_table[ct].dev->ifindex; + nhp->rtnh_ifindex = init_net.ipv4.vif_table[ct].dev->ifindex; nhp->rtnh_len = sizeof(*nhp); } } @@ -1672,11 +1670,11 @@ struct ipmr_vif_iter { static struct vif_device *ipmr_vif_seq_idx(struct ipmr_vif_iter *iter, loff_t pos) { - for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) { - if (!VIF_EXISTS(iter->ct)) + for (iter->ct = 0; iter->ct < init_net.ipv4.maxvif; ++iter->ct) { + if (!VIF_EXISTS(&init_net, iter->ct)) continue; if (pos-- == 0) - return &vif_table[iter->ct]; + return &init_net.ipv4.vif_table[iter->ct]; } return NULL; } @@ -1697,10 +1695,10 @@ static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (v == SEQ_START_TOKEN) return ipmr_vif_seq_idx(iter, 0); - while (++iter->ct < maxvif) { - if (!VIF_EXISTS(iter->ct)) + while (++iter->ct < init_net.ipv4.maxvif) { + if (!VIF_EXISTS(&init_net, iter->ct)) continue; - return &vif_table[iter->ct]; + return &init_net.ipv4.vif_table[iter->ct]; } return NULL; } @@ -1722,7 +1720,7 @@ static int ipmr_vif_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", - vif - vif_table, + vif - init_net.ipv4.vif_table, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out, vif->flags, vif->local, vif->remote); @@ -1864,9 +1862,9 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) mfc->mfc_un.res.wrong_if); for (n = mfc->mfc_un.res.minvif; n < mfc->mfc_un.res.maxvif; n++ ) { - if (VIF_EXISTS(n) - && mfc->mfc_un.res.ttls[n] < 255) - seq_printf(seq, + if (VIF_EXISTS(&init_net, n) && + mfc->mfc_un.res.ttls[n] < 255) + seq_printf(seq, " %2d:%-3d", n, mfc->mfc_un.res.ttls[n]); } @@ -1913,6 +1911,29 @@ static struct net_protocol pim_protocol = { /* * Setup for IP multicast routing */ +static int __net_init ipmr_net_init(struct net *net) +{ + int err = 0; + + net->ipv4.vif_table = kcalloc(MAXVIFS, sizeof(struct vif_device), + GFP_KERNEL); + if (!net->ipv4.vif_table) { + err = -ENOMEM; + goto fail; + } +fail: + return err; +} + +static void __net_exit ipmr_net_exit(struct net *net) +{ + kfree(net->ipv4.vif_table); +} + +static struct pernet_operations ipmr_net_ops = { + .init = ipmr_net_init, + .exit = ipmr_net_exit, +}; int __init ip_mr_init(void) { @@ -1925,6 +1946,10 @@ int __init ip_mr_init(void) if (!mrt_cachep) return -ENOMEM; + err = register_pernet_subsys(&ipmr_net_ops); + if (err) + goto reg_pernet_fail; + setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0); err = register_netdevice_notifier(&ip_mr_notifier); if (err) @@ -1945,6 +1970,8 @@ proc_vif_fail: #endif reg_notif_fail: del_timer(&ipmr_expire_timer); + unregister_pernet_subsys(&ipmr_net_ops); +reg_pernet_fail: kmem_cache_destroy(mrt_cachep); return err; } -- cgit v1.2.3-70-g09d2 From 2bb8b26c3ea8bde1943dc5cd4dda2dc9f48fb281 Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:18 +0000 Subject: netns: ipmr: dynamically allocate mfc_cache_array Preliminary work to make IPv4 multicast routing netns-aware. Dynamically allocate IPv4 multicast forwarding cache, mfc_cache_array, and move it to struct netns_ipv4. At the moment, mfc_cache_array is only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + net/ipv4/ipmr.c | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index edbcccbbc31..0b4285ac29d 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -57,6 +57,7 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MROUTE struct sock *mroute_sk; + struct mfc_cache **mfc_cache_array; struct vif_device *vif_table; int maxvif; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8428a0fb5c1..35b868dd3bf 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -82,8 +82,6 @@ static DEFINE_RWLOCK(mrt_lock); static int mroute_do_assert; /* Set in PIM assert */ static int mroute_do_pim; -static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */ - static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */ static atomic_t cache_resolve_queue_len; /* Size of unresolved */ @@ -524,7 +522,7 @@ static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp) int line = MFC_HASH(mcastgrp, origin); struct mfc_cache *c; - for (c=mfc_cache_array[line]; c; c = c->next) { + for (c = init_net.ipv4.mfc_cache_array[line]; c; c = c->next) { if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp) break; } @@ -764,7 +762,8 @@ static int ipmr_mfc_delete(struct mfcctl *mfc) line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) { + for (cp = &init_net.ipv4.mfc_cache_array[line]; + (c = *cp) != NULL; cp = &c->next) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { write_lock_bh(&mrt_lock); @@ -785,7 +784,8 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) { + for (cp = &init_net.ipv4.mfc_cache_array[line]; + (c = *cp) != NULL; cp = &c->next) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) break; @@ -816,8 +816,8 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) c->mfc_flags |= MFC_STATIC; write_lock_bh(&mrt_lock); - c->next = mfc_cache_array[line]; - mfc_cache_array[line] = c; + c->next = init_net.ipv4.mfc_cache_array[line]; + init_net.ipv4.mfc_cache_array[line] = c; write_unlock_bh(&mrt_lock); /* @@ -866,7 +866,7 @@ static void mroute_clean_tables(struct sock *sk) for (i=0; imfc_flags&MFC_STATIC) { cp = &c->next; @@ -1767,10 +1767,11 @@ static struct mfc_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos) { struct mfc_cache *mfc; - it->cache = mfc_cache_array; + it->cache = init_net.ipv4.mfc_cache_array; read_lock(&mrt_lock); for (it->ct = 0; it->ct < MFC_LINES; it->ct++) - for (mfc = mfc_cache_array[it->ct]; mfc; mfc = mfc->next) + for (mfc = init_net.ipv4.mfc_cache_array[it->ct]; + mfc; mfc = mfc->next) if (pos-- == 0) return mfc; read_unlock(&mrt_lock); @@ -1812,10 +1813,10 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (it->cache == &mfc_unres_queue) goto end_of_list; - BUG_ON(it->cache != mfc_cache_array); + BUG_ON(it->cache != init_net.ipv4.mfc_cache_array); while (++it->ct < MFC_LINES) { - mfc = mfc_cache_array[it->ct]; + mfc = init_net.ipv4.mfc_cache_array[it->ct]; if (mfc) return mfc; } @@ -1843,7 +1844,7 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) if (it->cache == &mfc_unres_queue) spin_unlock_bh(&mfc_unres_lock); - else if (it->cache == mfc_cache_array) + else if (it->cache == init_net.ipv4.mfc_cache_array) read_unlock(&mrt_lock); } @@ -1929,12 +1930,26 @@ static int __net_init ipmr_net_init(struct net *net) err = -ENOMEM; goto fail; } + + /* Forwarding cache */ + net->ipv4.mfc_cache_array = kcalloc(MFC_LINES, + sizeof(struct mfc_cache *), + GFP_KERNEL); + if (!net->ipv4.mfc_cache_array) { + err = -ENOMEM; + goto fail_mfc_cache; + } + return 0; + +fail_mfc_cache: + kfree(net->ipv4.vif_table); fail: return err; } static void __net_exit ipmr_net_exit(struct net *net) { + kfree(net->ipv4.mfc_cache_array); kfree(net->ipv4.vif_table); } -- cgit v1.2.3-70-g09d2 From 1e8fb3b6a4ac6c5e486298d88289038456957545 Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:19 +0000 Subject: netns: ipmr: declare counter cache_resolve_queue_len per-namespace Preliminary work to make IPv4 multicast routing netns-aware. Declare variable cache_resolve_queue_len per-namespace: move it into struct netns_ipv4. This variable counts the number of unresolved cache entries queued in the list mfc_unres_queue. This list is kept global to all netns as the number of entries per namespace is limited to 10 (hardcoded in routine ipmr_cache_unresolved). Entries belonging to different namespaces in mfc_unres_queue will be identified by matching the mfc_net member introduced previously in struct mfc_cache. Keeping this list global to all netns, also allows us to keep a single timer (ipmr_expire_timer) to handle their expiration. In some places cache_resolve_queue_len value was tested for arming or deleting the timer. These tests were equivalent to testing mfc_unres_queue value instead and are replaced in this patch. At the moment, cache_resolve_queue_len is only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + net/ipv4/ipmr.c | 39 +++++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 0b4285ac29d..90b7cd20aa6 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -60,6 +60,7 @@ struct netns_ipv4 { struct mfc_cache **mfc_cache_array; struct vif_device *vif_table; int maxvif; + atomic_t cache_resolve_queue_len; #endif }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 35b868dd3bf..feafd14eb7b 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -83,7 +83,6 @@ static int mroute_do_assert; /* Set in PIM assert */ static int mroute_do_pim; static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */ -static atomic_t cache_resolve_queue_len; /* Size of unresolved */ /* Special spinlock for queue of unresolved entries */ static DEFINE_SPINLOCK(mfc_unres_lock); @@ -340,7 +339,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c) struct sk_buff *skb; struct nlmsgerr *e; - atomic_dec(&cache_resolve_queue_len); + atomic_dec(&init_net.ipv4.cache_resolve_queue_len); while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { if (ip_hdr(skb)->version == 0) { @@ -374,7 +373,7 @@ static void ipmr_expire_process(unsigned long dummy) return; } - if (atomic_read(&cache_resolve_queue_len) == 0) + if (mfc_unres_queue == NULL) goto out; now = jiffies; @@ -395,7 +394,7 @@ static void ipmr_expire_process(unsigned long dummy) ipmr_destroy_unres(c); } - if (atomic_read(&cache_resolve_queue_len)) + if (mfc_unres_queue != NULL) mod_timer(&ipmr_expire_timer, jiffies + expires); out: @@ -690,7 +689,8 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) spin_lock_bh(&mfc_unres_lock); for (c=mfc_unres_queue; c; c=c->next) { - if (c->mfc_mcastgrp == iph->daddr && + if (net_eq(mfc_net(c), &init_net) && + c->mfc_mcastgrp == iph->daddr && c->mfc_origin == iph->saddr) break; } @@ -700,7 +700,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) * Create a new entry if allowable */ - if (atomic_read(&cache_resolve_queue_len) >= 10 || + if (atomic_read(&init_net.ipv4.cache_resolve_queue_len) >= 10 || (c = ipmr_cache_alloc_unres(&init_net)) == NULL) { spin_unlock_bh(&mfc_unres_lock); @@ -729,7 +729,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) return err; } - atomic_inc(&cache_resolve_queue_len); + atomic_inc(&init_net.ipv4.cache_resolve_queue_len); c->next = mfc_unres_queue; mfc_unres_queue = c; @@ -827,14 +827,16 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) spin_lock_bh(&mfc_unres_lock); for (cp = &mfc_unres_queue; (uc=*cp) != NULL; cp = &uc->next) { - if (uc->mfc_origin == c->mfc_origin && + if (net_eq(mfc_net(uc), &init_net) && + uc->mfc_origin == c->mfc_origin && uc->mfc_mcastgrp == c->mfc_mcastgrp) { *cp = uc->next; - if (atomic_dec_and_test(&cache_resolve_queue_len)) - del_timer(&ipmr_expire_timer); + atomic_dec(&init_net.ipv4.cache_resolve_queue_len); break; } } + if (mfc_unres_queue == NULL) + del_timer(&ipmr_expire_timer); spin_unlock_bh(&mfc_unres_lock); if (uc) { @@ -880,18 +882,19 @@ static void mroute_clean_tables(struct sock *sk) } } - if (atomic_read(&cache_resolve_queue_len) != 0) { - struct mfc_cache *c; + if (atomic_read(&init_net.ipv4.cache_resolve_queue_len) != 0) { + struct mfc_cache *c, **cp; spin_lock_bh(&mfc_unres_lock); - while (mfc_unres_queue != NULL) { - c = mfc_unres_queue; - mfc_unres_queue = c->next; - spin_unlock_bh(&mfc_unres_lock); + cp = &mfc_unres_queue; + while ((c = *cp) != NULL) { + if (!net_eq(mfc_net(c), &init_net)) { + cp = &c->next; + continue; + } + *cp = c->next; ipmr_destroy_unres(c); - - spin_lock_bh(&mfc_unres_lock); } spin_unlock_bh(&mfc_unres_lock); } -- cgit v1.2.3-70-g09d2 From 6f9374a9342e896c68df7cf7c0b039ab5cca994c Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:20 +0000 Subject: netns: ipmr: declare mroute_do_assert and mroute_do_pim per-namespace Preliminary work to make IPv4 multicast routing netns-aware. Declare IPv multicast routing variables 'mroute_do_assert' and 'mroute_do_pim' per-namespace in struct netns_ipv4. At the moment, these variables are only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 ++ net/ipv4/ipmr.c | 24 +++++++++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 90b7cd20aa6..8451722782b 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -61,6 +61,8 @@ struct netns_ipv4 { struct vif_device *vif_table; int maxvif; atomic_t cache_resolve_queue_len; + int mroute_do_assert; + int mroute_do_pim; #endif }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index feafd14eb7b..d6a28acc068 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -79,9 +79,6 @@ static DEFINE_RWLOCK(mrt_lock); #define VIF_EXISTS(_net, _idx) ((_net)->ipv4.vif_table[_idx].dev != NULL) -static int mroute_do_assert; /* Set in PIM assert */ -static int mroute_do_pim; - static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */ /* Special spinlock for queue of unresolved entries */ @@ -1003,7 +1000,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int int v; if (get_user(v,(int __user *)optval)) return -EFAULT; - mroute_do_assert=(v)?1:0; + init_net.ipv4.mroute_do_assert = (v) ? 1 : 0; return 0; } #ifdef CONFIG_IP_PIMSM @@ -1017,11 +1014,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int rtnl_lock(); ret = 0; - if (v != mroute_do_pim) { - mroute_do_pim = v; - mroute_do_assert = v; + if (v != init_net.ipv4.mroute_do_pim) { + init_net.ipv4.mroute_do_pim = v; + init_net.ipv4.mroute_do_assert = v; #ifdef CONFIG_IP_PIMSM_V2 - if (mroute_do_pim) + if (init_net.ipv4.mroute_do_pim) ret = inet_add_protocol(&pim_protocol, IPPROTO_PIM); else @@ -1073,10 +1070,10 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int val = 0x0305; #ifdef CONFIG_IP_PIMSM else if (optname == MRT_PIM) - val = mroute_do_pim; + val = init_net.ipv4.mroute_do_pim; #endif else - val = mroute_do_assert; + val = init_net.ipv4.mroute_do_assert; if (copy_to_user(optval, &val, olr)) return -EFAULT; return 0; @@ -1356,13 +1353,14 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local cache->mfc_un.res.wrong_if++; true_vifi = ipmr_find_vif(skb->dev); - if (true_vifi >= 0 && mroute_do_assert && + if (true_vifi >= 0 && init_net.ipv4.mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, so that we cannot check that packet arrived on an oif. It is bad, but otherwise we would need to move pretty large chunk of pimd to kernel. Ough... --ANK */ - (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) && + (init_net.ipv4.mroute_do_pim || + cache->mfc_un.res.ttls[true_vifi] < 255) && time_after(jiffies, cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { cache->mfc_un.res.last_assert = jiffies; @@ -1550,7 +1548,7 @@ int pim_rcv_v1(struct sk_buff * skb) pim = igmp_hdr(skb); - if (!mroute_do_pim || + if (!init_net.ipv4.mroute_do_pim || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; -- cgit v1.2.3-70-g09d2 From 6c5143dbcfe50ac722965dc7d096abbeeec8bb33 Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:21 +0000 Subject: netns: ipmr: declare reg_vif_num per-namespace Preliminary work to make IPv4 multicast routing netns-aware. Declare variable 'reg_vif_num' per-namespace, move into struct netns_ipv4. At the moment, this variable is only referenced in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 3 +++ net/ipv4/ipmr.c | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 8451722782b..2eb3814d625 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -63,6 +63,9 @@ struct netns_ipv4 { atomic_t cache_resolve_queue_len; int mroute_do_assert; int mroute_do_pim; +#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) + int mroute_reg_vif_num; +#endif #endif }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d6a28acc068..346e67b50d6 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -197,14 +197,12 @@ failure: #ifdef CONFIG_IP_PIMSM -static int reg_vif_num = -1; - static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; dev->stats.tx_packets++; - ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT); + ipmr_cache_report(skb, init_net.ipv4.mroute_reg_vif_num, IGMPMSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); return 0; @@ -292,8 +290,8 @@ static int vif_delete(int vifi, int notify) } #ifdef CONFIG_IP_PIMSM - if (vifi == reg_vif_num) - reg_vif_num = -1; + if (vifi == init_net.ipv4.mroute_reg_vif_num) + init_net.ipv4.mroute_reg_vif_num = -1; #endif if (vifi+1 == init_net.ipv4.maxvif) { @@ -439,7 +437,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) * Special Purpose VIF in PIM * All the packets will be sent to the daemon */ - if (reg_vif_num >= 0) + if (init_net.ipv4.mroute_reg_vif_num >= 0) return -EADDRINUSE; dev = ipmr_reg_vif(); if (!dev) @@ -505,7 +503,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) v->dev = dev; #ifdef CONFIG_IP_PIMSM if (v->flags&VIFF_REGISTER) - reg_vif_num = vifi; + init_net.ipv4.mroute_reg_vif_num = vifi; #endif if (vifi+1 > init_net.ipv4.maxvif) init_net.ipv4.maxvif = vifi+1; @@ -623,7 +621,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); msg->im_msgtype = IGMPMSG_WHOLEPKT; msg->im_mbz = 0; - msg->im_vif = reg_vif_num; + msg->im_vif = init_net.ipv4.mroute_reg_vif_num; ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + sizeof(struct iphdr)); @@ -1506,8 +1504,8 @@ static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) return 1; read_lock(&mrt_lock); - if (reg_vif_num >= 0) - reg_dev = init_net.ipv4.vif_table[reg_vif_num].dev; + if (init_net.ipv4.mroute_reg_vif_num >= 0) + reg_dev = init_net.ipv4.vif_table[init_net.ipv4.mroute_reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); @@ -1940,6 +1938,10 @@ static int __net_init ipmr_net_init(struct net *net) err = -ENOMEM; goto fail_mfc_cache; } + +#ifdef CONFIG_IP_PIMSM + net->ipv4.mroute_reg_vif_num = -1; +#endif return 0; fail_mfc_cache: -- cgit v1.2.3-70-g09d2 From 76e02cf6945e6faa9f6b546dc0513512197c5966 Mon Sep 17 00:00:00 2001 From: "remi.denis-courmont@nokia" Date: Fri, 23 Jan 2009 03:00:27 +0000 Subject: Phonet: allow phonet_device_init() to fail, put it to __init section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/phonet.h | 1 - include/net/phonet/pn_dev.h | 3 ++- net/phonet/af_phonet.c | 9 ++++++--- net/phonet/pn_dev.c | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h index 057b0a8a288..d43f71b5ec0 100644 --- a/include/net/phonet/phonet.h +++ b/include/net/phonet/phonet.h @@ -105,7 +105,6 @@ void phonet_proto_unregister(int protocol, struct phonet_protocol *pp); int phonet_sysctl_init(void); void phonet_sysctl_exit(void); -void phonet_netlink_register(void); int isi_register(void); void isi_unregister(void); diff --git a/include/net/phonet/pn_dev.h b/include/net/phonet/pn_dev.h index aa1c59a1d33..59ae628b111 100644 --- a/include/net/phonet/pn_dev.h +++ b/include/net/phonet/pn_dev.h @@ -36,8 +36,9 @@ struct phonet_device { DECLARE_BITMAP(addrs, 64); }; -void phonet_device_init(void); +int phonet_device_init(void); void phonet_device_exit(void); +void phonet_netlink_register(void); struct net_device *phonet_device_get(struct net *net); int phonet_address_add(struct net_device *dev, u8 addr); diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index c7c39d92ee5..95bc49ddb8b 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -426,16 +426,18 @@ static int __init phonet_init(void) { int err; + err = phonet_device_init(); + if (err) + return err; + err = sock_register(&phonet_proto_family); if (err) { printk(KERN_ALERT "phonet protocol family initialization failed\n"); - return err; + goto err_sock; } - phonet_device_init(); dev_add_pack(&phonet_packet_type); - phonet_netlink_register(); phonet_sysctl_init(); err = isi_register(); @@ -447,6 +449,7 @@ err: phonet_sysctl_exit(); sock_unregister(PF_PHONET); dev_remove_pack(&phonet_packet_type); +err_sock: phonet_device_exit(); return err; } diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 5491bf5e354..af49db01d63 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -188,9 +188,11 @@ static struct notifier_block phonet_device_notifier = { }; /* Initialize Phonet devices list */ -void phonet_device_init(void) +int __init phonet_device_init(void) { register_netdevice_notifier(&phonet_device_notifier); + phonet_netlink_register(); + return 0; } void phonet_device_exit(void) -- cgit v1.2.3-70-g09d2 From 660f706d931d4795d341805e083a8091af74fa88 Mon Sep 17 00:00:00 2001 From: "remi.denis-courmont@nokia" Date: Fri, 23 Jan 2009 03:00:28 +0000 Subject: Phonet: handle rtnetlink registration failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pn_dev.h | 2 +- net/phonet/pn_dev.c | 8 ++++++-- net/phonet/pn_netlink.c | 13 +++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/phonet/pn_dev.h b/include/net/phonet/pn_dev.h index 59ae628b111..4ba2aedaa50 100644 --- a/include/net/phonet/pn_dev.h +++ b/include/net/phonet/pn_dev.h @@ -38,7 +38,7 @@ struct phonet_device { int phonet_device_init(void); void phonet_device_exit(void); -void phonet_netlink_register(void); +int phonet_netlink_register(void); struct net_device *phonet_device_get(struct net *net); int phonet_address_add(struct net_device *dev, u8 addr); diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index af49db01d63..fd418107652 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -190,9 +190,13 @@ static struct notifier_block phonet_device_notifier = { /* Initialize Phonet devices list */ int __init phonet_device_init(void) { + int err; + register_netdevice_notifier(&phonet_device_notifier); - phonet_netlink_register(); - return 0; + err = phonet_netlink_register(); + if (err) + phonet_device_exit(); + return err; } void phonet_device_exit(void) diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 242fe8f8c32..918a4f07f24 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -160,9 +160,14 @@ out: return skb->len; } -void __init phonet_netlink_register(void) +int __init phonet_netlink_register(void) { - rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); - rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); - rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); + int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); + if (err) + return err; + + /* Further __rtnl_register() cannot fail */ + __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); + __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); + return 0; } -- cgit v1.2.3-70-g09d2 From 9a3b7a42bb2919a6282a96a5f4abe0f9be36c4b3 Mon Sep 17 00:00:00 2001 From: "remi.denis-courmont@nokia" Date: Fri, 23 Jan 2009 03:00:30 +0000 Subject: Phonet: use per-namespace devices list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pn_dev.h | 2 +- net/phonet/pn_dev.c | 108 ++++++++++++++++++++++++++++++-------------- net/phonet/pn_netlink.c | 11 ++--- 3 files changed, 81 insertions(+), 40 deletions(-) (limited to 'include/net') diff --git a/include/net/phonet/pn_dev.h b/include/net/phonet/pn_dev.h index 4ba2aedaa50..5054dc5ea2c 100644 --- a/include/net/phonet/pn_dev.h +++ b/include/net/phonet/pn_dev.h @@ -28,7 +28,7 @@ struct phonet_device_list { spinlock_t lock; }; -extern struct phonet_device_list pndevs; +struct phonet_device_list *phonet_device_list(struct net *net); struct phonet_device { struct list_head list; diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 3e24c0522ee..80a322d7790 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -28,32 +28,41 @@ #include #include #include +#include #include -/* when accessing, remember to lock with spin_lock(&pndevs.lock); */ -struct phonet_device_list pndevs = { - .list = LIST_HEAD_INIT(pndevs.list), - .lock = __SPIN_LOCK_UNLOCKED(pndevs.lock), +struct phonet_net { + struct phonet_device_list pndevs; }; +int phonet_net_id; + +struct phonet_device_list *phonet_device_list(struct net *net) +{ + struct phonet_net *pnn = net_generic(net, phonet_net_id); + return &pnn->pndevs; +} + /* Allocate new Phonet device. */ static struct phonet_device *__phonet_device_alloc(struct net_device *dev) { + struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); if (pnd == NULL) return NULL; pnd->netdev = dev; bitmap_zero(pnd->addrs, 64); - list_add(&pnd->list, &pndevs.list); + list_add(&pnd->list, &pndevs->list); return pnd; } static struct phonet_device *__phonet_get(struct net_device *dev) { + struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; - list_for_each_entry(pnd, &pndevs.list, list) { + list_for_each_entry(pnd, &pndevs->list, list) { if (pnd->netdev == dev) return pnd; } @@ -68,32 +77,33 @@ static void __phonet_device_free(struct phonet_device *pnd) struct net_device *phonet_device_get(struct net *net) { + struct phonet_device_list *pndevs = phonet_device_list(net); struct phonet_device *pnd; struct net_device *dev; - spin_lock_bh(&pndevs.lock); - list_for_each_entry(pnd, &pndevs.list, list) { + spin_lock_bh(&pndevs->lock); + list_for_each_entry(pnd, &pndevs->list, list) { dev = pnd->netdev; BUG_ON(!dev); - if (net_eq(dev_net(dev), net) && - (dev->reg_state == NETREG_REGISTERED) && + if ((dev->reg_state == NETREG_REGISTERED) && ((pnd->netdev->flags & IFF_UP)) == IFF_UP) break; dev = NULL; } if (dev) dev_hold(dev); - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); return dev; } int phonet_address_add(struct net_device *dev, u8 addr) { + struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; int err = 0; - spin_lock_bh(&pndevs.lock); + spin_lock_bh(&pndevs->lock); /* Find or create Phonet-specific device data */ pnd = __phonet_get(dev); if (pnd == NULL) @@ -102,31 +112,33 @@ int phonet_address_add(struct net_device *dev, u8 addr) err = -ENOMEM; else if (test_and_set_bit(addr >> 2, pnd->addrs)) err = -EEXIST; - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); return err; } int phonet_address_del(struct net_device *dev, u8 addr) { + struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; int err = 0; - spin_lock_bh(&pndevs.lock); + spin_lock_bh(&pndevs->lock); pnd = __phonet_get(dev); if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) err = -EADDRNOTAVAIL; else if (bitmap_empty(pnd->addrs, 64)) __phonet_device_free(pnd); - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); return err; } /* Gets a source address toward a destination, through a interface. */ u8 phonet_address_get(struct net_device *dev, u8 addr) { + struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; - spin_lock_bh(&pndevs.lock); + spin_lock_bh(&pndevs->lock); pnd = __phonet_get(dev); if (pnd) { BUG_ON(bitmap_empty(pnd->addrs, 64)); @@ -136,30 +148,31 @@ u8 phonet_address_get(struct net_device *dev, u8 addr) addr = find_first_bit(pnd->addrs, 64) << 2; } else addr = PN_NO_ADDR; - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); return addr; } int phonet_address_lookup(struct net *net, u8 addr) { + struct phonet_device_list *pndevs = phonet_device_list(net); struct phonet_device *pnd; + int err = -EADDRNOTAVAIL; - spin_lock_bh(&pndevs.lock); - list_for_each_entry(pnd, &pndevs.list, list) { - if (!net_eq(dev_net(pnd->netdev), net)) - continue; + spin_lock_bh(&pndevs->lock); + list_for_each_entry(pnd, &pndevs->list, list) { /* Don't allow unregistering devices! */ if ((pnd->netdev->reg_state != NETREG_REGISTERED) || ((pnd->netdev->flags & IFF_UP)) != IFF_UP) continue; if (test_bit(addr >> 2, pnd->addrs)) { - spin_unlock_bh(&pndevs.lock); - return 0; + err = 0; + goto found; } } - spin_unlock_bh(&pndevs.lock); - return -EADDRNOTAVAIL; +found: + spin_unlock_bh(&pndevs->lock); + return err; } /* notify Phonet of device events */ @@ -169,14 +182,16 @@ static int phonet_device_notify(struct notifier_block *me, unsigned long what, struct net_device *dev = arg; if (what == NETDEV_UNREGISTER) { + struct phonet_device_list *pndevs; struct phonet_device *pnd; /* Destroy phonet-specific device data */ - spin_lock_bh(&pndevs.lock); + pndevs = phonet_device_list(dev_net(dev)); + spin_lock_bh(&pndevs->lock); pnd = __phonet_get(dev); if (pnd) __phonet_device_free(pnd); - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); } return 0; @@ -187,10 +202,41 @@ static struct notifier_block phonet_device_notifier = { .priority = 0, }; +/* Per-namespace Phonet devices handling */ +static int phonet_init_net(struct net *net) +{ + struct phonet_net *pnn = kmalloc(sizeof(*pnn), GFP_KERNEL); + if (!pnn) + return -ENOMEM; + + INIT_LIST_HEAD(&pnn->pndevs.list); + spin_lock_init(&pnn->pndevs.lock); + net_assign_generic(net, phonet_net_id, pnn); + return 0; +} + +static void phonet_exit_net(struct net *net) +{ + struct phonet_net *pnn = net_generic(net, phonet_net_id); + struct phonet_device *pnd, *n; + + list_for_each_entry_safe(pnd, n, &pnn->pndevs.list, list) + __phonet_device_free(pnd); + + kfree(pnn); +} + +static struct pernet_operations phonet_net_ops = { + .init = phonet_init_net, + .exit = phonet_exit_net, +}; + /* Initialize Phonet devices list */ int __init phonet_device_init(void) { - int err; + int err = register_pernet_gen_device(&phonet_net_id, &phonet_net_ops); + if (err) + return err; register_netdevice_notifier(&phonet_device_notifier); err = phonet_netlink_register(); @@ -201,11 +247,7 @@ int __init phonet_device_init(void) void phonet_device_exit(void) { - struct phonet_device *pnd, *n; - rtnl_unregister_all(PF_PHONET); unregister_netdevice_notifier(&phonet_device_notifier); - - list_for_each_entry_safe(pnd, n, &pndevs.list, list) - __phonet_device_free(pnd); + unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops); } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 918a4f07f24..1ceea1f9241 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -123,17 +123,16 @@ nla_put_failure: static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { - struct net *net = sock_net(skb->sk); + struct phonet_device_list *pndevs; struct phonet_device *pnd; int dev_idx = 0, dev_start_idx = cb->args[0]; int addr_idx = 0, addr_start_idx = cb->args[1]; - spin_lock_bh(&pndevs.lock); - list_for_each_entry(pnd, &pndevs.list, list) { + pndevs = phonet_device_list(sock_net(skb->sk)); + spin_lock_bh(&pndevs->lock); + list_for_each_entry(pnd, &pndevs->list, list) { u8 addr; - if (!net_eq(dev_net(pnd->netdev), net)) - continue; if (dev_idx > dev_start_idx) addr_start_idx = 0; if (dev_idx++ < dev_start_idx) @@ -153,7 +152,7 @@ static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) } out: - spin_unlock_bh(&pndevs.lock); + spin_unlock_bh(&pndevs->lock); cb->args[0] = dev_idx; cb->args[1] = addr_idx; -- cgit v1.2.3-70-g09d2 From 10c806b32db1c9f010945e92043ef2a3f6fffc3f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 23 Dec 2008 15:58:36 -0800 Subject: mac80211: add HT conf helpers In HT capable drivers you often need to check if you are currently using HT20 or HT40. This adds a few small helpers to let drivers figure that out. Signed-off-by: Luis R. Rodriguez Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 559422fc094..1e8db8ae615 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1962,4 +1962,34 @@ rate_lowest_index(struct ieee80211_supported_band *sband, int ieee80211_rate_control_register(struct rate_control_ops *ops); void ieee80211_rate_control_unregister(struct rate_control_ops *ops); +static inline bool +conf_is_ht20(struct ieee80211_conf *conf) +{ + return conf->ht.channel_type == NL80211_CHAN_HT20; +} + +static inline bool +conf_is_ht40_minus(struct ieee80211_conf *conf) +{ + return conf->ht.channel_type == NL80211_CHAN_HT40MINUS; +} + +static inline bool +conf_is_ht40_plus(struct ieee80211_conf *conf) +{ + return conf->ht.channel_type == NL80211_CHAN_HT40PLUS; +} + +static inline bool +conf_is_ht40(struct ieee80211_conf *conf) +{ + return conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf); +} + +static inline bool +conf_is_ht(struct ieee80211_conf *conf) +{ + return conf->ht.channel_type != NL80211_CHAN_NO_HT; +} + #endif /* MAC80211_H */ -- cgit v1.2.3-70-g09d2 From 285256a59d790c6a9afe8ec82804a369d956ac06 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 23 Dec 2008 15:58:45 -0800 Subject: mac80211: no need for ht.enabled We can simply use conf_is_ht() check where needed. Signed-off-by: Luis R. Rodriguez Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 1 - net/mac80211/ht.c | 3 +-- net/mac80211/main.c | 10 ---------- net/mac80211/mlme.c | 1 - 4 files changed, 1 insertion(+), 14 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1e8db8ae615..9d67fdf1c26 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -508,7 +508,6 @@ static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) #define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME()) struct ieee80211_ht_conf { - bool enabled; enum nl80211_channel_type channel_type; }; diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index c5c0c527109..f6547de5ac6 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -130,11 +130,10 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, } } - ht_changed = local->hw.conf.ht.enabled != enable_ht || + ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || channel_type != local->hw.conf.ht.channel_type; local->oper_channel_type = channel_type; - local->hw.conf.ht.enabled = enable_ht; if (ht_changed) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 24b14363d6e..a6cb480dda0 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -211,16 +211,6 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) channel_type != local->hw.conf.ht.channel_type) { local->hw.conf.channel = chan; local->hw.conf.ht.channel_type = channel_type; - switch (channel_type) { - case NL80211_CHAN_NO_HT: - local->hw.conf.ht.enabled = false; - break; - case NL80211_CHAN_HT20: - case NL80211_CHAN_HT40MINUS: - case NL80211_CHAN_HT40PLUS: - local->hw.conf.ht.enabled = true; - break; - } changed |= IEEE80211_CONF_CHANGE_CHANNEL; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 599a42172a1..12976026cc4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -901,7 +901,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); - local->hw.conf.ht.enabled = false; local->oper_channel_type = NL80211_CHAN_NO_HT; config_changed |= IEEE80211_CONF_CHANGE_HT; -- cgit v1.2.3-70-g09d2 From e3c92df08cbf6a0cb60a9c7ce377378383967e07 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 24 Dec 2008 13:53:11 +0530 Subject: mac80211: Fix tx power setting power_level in ieee80211_conf is being used for more than one purpose. It being used as user configured power limit and the final power limit given to the driver. By doing so, except very first time, the tx power limit is taken from min(chan->max_power, local->hw.conf.power_level) which is not what we want. This patch defines a new memeber in ieee80211_conf which is meant only for user configured power limit. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- include/net/mac80211.h | 2 ++ net/mac80211/main.c | 4 ++-- net/mac80211/wext.c | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9d67fdf1c26..ffcbd12775a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -546,6 +546,7 @@ enum ieee80211_conf_changed { * @listen_interval: listen interval in units of beacon interval * @flags: configuration flags defined above * @power_level: requested transmit power (in dBm) + * @user_power_level: User configured transmit power (in dBm) * @channel: the channel to tune to * @ht: the HT configuration for the device * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame @@ -559,6 +560,7 @@ struct ieee80211_conf { int beacon_int; u32 flags; int power_level; + int user_power_level; u16 listen_interval; bool radio_enabled; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a6cb480dda0..dca4b7da6ca 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -214,10 +214,10 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) changed |= IEEE80211_CONF_CHANGE_CHANNEL; } - if (!local->hw.conf.power_level) + if (!local->hw.conf.user_power_level) power = chan->max_power; else - power = min(chan->max_power, local->hw.conf.power_level); + power = min(chan->max_power, local->hw.conf.user_power_level); if (local->hw.conf.power_level != power) { changed |= IEEE80211_CONF_CHANGE_POWER; local->hw.conf.power_level = power; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 48fc6b9a62a..654041b9373 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -549,10 +549,9 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev, else /* Automatic power level setting */ new_power_level = chan->max_power; - if (local->hw.conf.power_level != new_power_level) { - local->hw.conf.power_level = new_power_level; + local->hw.conf.user_power_level = new_power_level; + if (local->hw.conf.power_level != new_power_level) reconf_flags |= IEEE80211_CONF_CHANGE_POWER; - } if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) { local->hw.conf.radio_enabled = !(data->txpower.disabled); -- cgit v1.2.3-70-g09d2 From dc822b5db479dc0178d5c04cbb656dad0b6564fb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 29 Dec 2008 12:55:09 +0100 Subject: mac80211: clean up set_key callback The set_key callback now seems rather odd, passing a MAC address instead of a station struct, and a local address instead of a vif struct. Change that. Signed-off-by: Johannes Berg Acked-by: Bob Copeland [ath5k] Acked-by: Ivo van Doorn [rt2x00] Acked-by: Christian Lamparter [p54] Tested-by: Kalle Valo [iwl3945] Tested-by: Samuel Ortiz [iwl3945] Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 9 ++--- drivers/net/wireless/ath5k/pcu.c | 2 +- drivers/net/wireless/ath9k/main.c | 18 ++++++---- drivers/net/wireless/b43/main.c | 13 +++++--- drivers/net/wireless/iwlwifi/iwl-agn.c | 10 +++--- drivers/net/wireless/iwlwifi/iwl3945-base.c | 12 ++++--- drivers/net/wireless/p54/p54common.c | 6 ++-- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 29 +++++++--------- include/net/mac80211.h | 20 +++++------ net/mac80211/key.c | 51 ++++++++++++++--------------- 11 files changed, 89 insertions(+), 83 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 8ef87356e08..88618645a7e 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -232,7 +232,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, int mc_count, struct dev_mc_list *mclist); static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); static int ath5k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); @@ -2991,8 +2991,8 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, - struct ieee80211_key_conf *key) + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) { struct ath5k_softc *sc = hw->priv; int ret = 0; @@ -3015,7 +3015,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, addr); + ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, + sta ? sta->addr : NULL); if (ret) { ATH5K_ERR(sc, "can't set the key\n"); goto unlock; diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 75eb9f43c74..5b416ed6529 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1139,7 +1139,7 @@ int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) /* MAC may be NULL if it's a broadcast key. In this case no need to * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ - if (unlikely(mac == NULL)) { + if (!mac) { low_id = 0xffffffff; high_id = 0xffff | AR5K_KEYTABLE_VALID; } else { diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 5cbda924556..a4046a97c01 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -794,7 +794,7 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc) } static int ath_key_config(struct ath_softc *sc, - const u8 *addr, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath9k_keyval hk; @@ -828,7 +828,10 @@ static int ath_key_config(struct ath_softc *sc, } else if (key->keyidx) { struct ieee80211_vif *vif; - mac = addr; + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + vif = sc->sc_vaps[0]; if (vif->type != NL80211_IFTYPE_AP) { /* Only keyidx 0 should be used with unicast key, but @@ -837,7 +840,10 @@ static int ath_key_config(struct ath_softc *sc, } else return -EIO; } else { - mac = addr; + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + if (key->alg == ALG_TKIP) idx = ath_reserve_key_cache_slot_tkip(sc); else @@ -2320,8 +2326,8 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, static int ath9k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, - const u8 *addr, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct ath_softc *sc = hw->priv; @@ -2331,7 +2337,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: - ret = ath_key_config(sc, addr, key); + ret = ath_key_config(sc, sta, key); if (ret >= 0) { key->hw_key_idx = ret; /* push IV and Michael MIC generation to stack */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 5ca55dcd034..19ad5164fce 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3476,8 +3476,8 @@ out_unlock_mutex: } static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, - struct ieee80211_key_conf *key) + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -3542,9 +3542,14 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + if (WARN_ON(!sta)) { + err = -EOPNOTSUPP; + goto out_unlock; + } /* Pairwise key with an assigned MAC address. */ err = b43_key_write(dev, -1, algorithm, - key->key, key->keylen, addr, key); + key->key, key->keylen, + sta->addr, key); } else { /* Group key */ err = b43_key_write(dev, index, algorithm, @@ -3577,7 +3582,7 @@ out_unlock: b43dbg(wl, "%s hardware based encryption for keyidx: %d, " "mac: %pM\n", cmd == SET_KEY ? "Using" : "Disabling", key->keyidx, - addr); + sta ? sta->addr : ""); b43_dump_keymemory(dev); } write_unlock(&wl->tx_lock); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index f3f6dba7a67..dbfee28107a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3021,13 +3021,17 @@ static void iwl_mac_update_tkip_key(struct ieee80211_hw *hw, } static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct iwl_priv *priv = hw->priv; int ret = 0; u8 sta_id = IWL_INVALID_STATION; u8 is_default_wep_key = 0; + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; + static const u8 *addr; IWL_DEBUG_MAC80211("enter\n"); @@ -3036,9 +3040,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return -EOPNOTSUPP; } - if (is_zero_ether_addr(addr)) - /* only support pairwise keys */ - return -EOPNOTSUPP; + addr = sta ? sta->addr : bcast_addr; sta_id = iwl_find_station(priv, addr); if (sta_id == IWL_INVALID_STATION) { diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a176f42fd7c..43cfc6ec516 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -6546,12 +6546,16 @@ out_unlock: } static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, - struct ieee80211_key_conf *key) + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) { struct iwl_priv *priv = hw->priv; + const u8 *addr; int rc = 0; u8 sta_id; + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; IWL_DEBUG_MAC80211("enter\n"); @@ -6560,9 +6564,7 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return -EOPNOTSUPP; } - if (is_zero_ether_addr(addr)) - /* only support pairwise keys */ - return -EOPNOTSUPP; + addr = sta ? sta->addr : bcast_addr; sta_id = iwl3945_hw_find_station(priv, addr); if (sta_id == IWL_INVALID_STATION) { diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 8d2df5b6ecb..0907e6f246e 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -2127,7 +2127,7 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, } static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct p54_common *priv = dev->priv; @@ -2191,8 +2191,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, rxkey->entry = key->keyidx; rxkey->key_id = key->keyidx; rxkey->key_type = algo; - if (address) - memcpy(rxkey->mac, address, ETH_ALEN); + if (sta) + memcpy(rxkey->mac, sta->addr, ETH_ALEN); else memset(rxkey->mac, ~0, ETH_ALEN); if (key->alg != ALG_TKIP) { diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 1ef3434a2ba..890c7216cf3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -931,7 +931,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mc_list); #ifdef CONFIG_RT2X00_LIB_CRYPTO int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); #else #define rt2x00mac_set_key NULL diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index bf7755a2164..3e204406f44 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -502,15 +502,17 @@ static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) } int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_sta *sta; + struct rt2x00_intf *intf = vif_to_intf(vif); int (*set_key) (struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); struct rt2x00lib_crypto crypto; + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; @@ -528,32 +530,25 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (rt2x00dev->intf_sta_count) crypto.bssidx = 0; else - crypto.bssidx = - local_address[5] & (rt2x00dev->ops->max_ap_intf - 1); + crypto.bssidx = intf->mac[5] & (rt2x00dev->ops->max_ap_intf - 1); crypto.cipher = rt2x00crypto_key_to_cipher(key); if (crypto.cipher == CIPHER_NONE) return -EOPNOTSUPP; crypto.cmd = cmd; - crypto.address = address; + + if (sta) { + /* some drivers need the AID */ + crypto.aid = sta->aid; + crypto.address = sta->addr; + } else + crypto.address = bcast_addr; if (crypto.cipher == CIPHER_TKIP) memcpy_tkip(&crypto, &key->key[0], key->keylen); else memcpy(&crypto.key, &key->key[0], key->keylen); - - /* - * Discover the Association ID from mac80211. - * Some drivers need this information when updating the - * hardware key (either adding or removing). - */ - rcu_read_lock(); - sta = ieee80211_find_sta(hw, address); - if (sta) - crypto.aid = sta->aid; - rcu_read_unlock(); - /* * Each BSS has a maximum of 4 shared keys. * Shared key index values: diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ffcbd12775a..9215b1ec90e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -715,8 +715,8 @@ enum ieee80211_key_flags { * - Temporal Encryption Key (128 bits) * - Temporal Authenticator Tx MIC Key (64 bits) * - Temporal Authenticator Rx MIC Key (64 bits) - * @icv_len: FIXME - * @iv_len: FIXME + * @icv_len: The ICV length for this key type + * @iv_len: The IV length for this key type */ struct ieee80211_key_conf { enum ieee80211_key_alg alg; @@ -1019,16 +1019,12 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * * The set_key() callback in the &struct ieee80211_ops for a given * device is called to enable hardware acceleration of encryption and - * decryption. The callback takes an @address parameter that will be - * the broadcast address for default keys, the other station's hardware - * address for individual keys or the zero address for keys that will - * be used only for transmission. + * decryption. The callback takes a @sta parameter that will be NULL + * for default keys or keys used for transmission only, or point to + * the station information for the peer for individual keys. * Multiple transmission keys with the same key index may be used when * VLANs are configured for an access point. * - * The @local_address parameter will always be set to our own address, - * this is only relevant if you support multiple local addresses. - * * When transmitting, the TX control data will use the @hw_key_idx * selected by the driver by modifying the &struct ieee80211_key_conf * pointed to by the @key parameter to the set_key() function. @@ -1233,8 +1229,8 @@ enum ieee80211_ampdu_mlme_action { * * @set_key: See the section "Hardware crypto acceleration" * This callback can sleep, and is only called between add_interface - * and remove_interface calls, i.e. while the interface with the - * given local_address is enabled. + * and remove_interface calls, i.e. while the given virtual interface + * is enabled. * * @update_tkip_key: See the section "Hardware crypto acceleration" * This callback will be called in the context of Rx. Called for drivers @@ -1311,7 +1307,7 @@ struct ieee80211_ops { int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key); void (*update_tkip_key)(struct ieee80211_hw *hw, struct ieee80211_key_conf *conf, const u8 *address, diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 999f7aa4232..b0a025c9b61 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -47,7 +47,6 @@ */ static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -static const u8 zero_addr[ETH_ALEN]; /* key mutex: used to synchronise todo runners */ static DEFINE_MUTEX(key_mutex); @@ -108,29 +107,18 @@ static void assert_key_lock(void) WARN_ON(!mutex_is_locked(&key_mutex)); } -static const u8 *get_mac_for_key(struct ieee80211_key *key) +static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) { - const u8 *addr = bcast_addr; - - /* - * If we're an AP we won't ever receive frames with a non-WEP - * group key so we tell the driver that by using the zero MAC - * address to indicate a transmit-only key. - */ - if (key->conf.alg != ALG_WEP && - (key->sdata->vif.type == NL80211_IFTYPE_AP || - key->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) - addr = zero_addr; - if (key->sta) - addr = key->sta->sta.addr; + return &key->sta->sta; - return addr; + return NULL; } static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) { - const u8 *addr; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_sta *sta; int ret; assert_key_lock(); @@ -139,11 +127,16 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) if (!key->local->ops->set_key) return; - addr = get_mac_for_key(key); + sta = get_sta_for_key(key); + + sdata = key->sdata; + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, - key->sdata->dev->dev_addr, addr, - &key->conf); + &sdata->vif, sta, &key->conf); if (!ret) { spin_lock(&todo_lock); @@ -155,12 +148,13 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) printk(KERN_ERR "mac80211-%s: failed to set key " "(%d, %pM) to hardware (%d)\n", wiphy_name(key->local->hw.wiphy), - key->conf.keyidx, addr, ret); + key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); } static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) { - const u8 *addr; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_sta *sta; int ret; assert_key_lock(); @@ -176,17 +170,22 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) } spin_unlock(&todo_lock); - addr = get_mac_for_key(key); + sta = get_sta_for_key(key); + sdata = key->sdata; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY, - key->sdata->dev->dev_addr, addr, - &key->conf); + &sdata->vif, sta, &key->conf); if (ret) printk(KERN_ERR "mac80211-%s: failed to remove key " "(%d, %pM) from hardware (%d)\n", wiphy_name(key->local->hw.wiphy), - key->conf.keyidx, addr, ret); + key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); spin_lock(&todo_lock); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; -- cgit v1.2.3-70-g09d2 From 2bf30fabadbdcb535b057afc92aba015884847dc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Jan 2009 23:23:56 +0100 Subject: mac80211: remove user_power_level from driver API I missed this during review of "mac80211: Fix tx power setting", the user_power_level shouldn't be available to the driver but rather be an internal value used to calculate the value for the driver. Signed-off-by: Johannes Berg Cc: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- include/net/mac80211.h | 2 -- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/main.c | 4 ++-- net/mac80211/wext.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9215b1ec90e..0ffe932942f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -546,7 +546,6 @@ enum ieee80211_conf_changed { * @listen_interval: listen interval in units of beacon interval * @flags: configuration flags defined above * @power_level: requested transmit power (in dBm) - * @user_power_level: User configured transmit power (in dBm) * @channel: the channel to tune to * @ht: the HT configuration for the device * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame @@ -560,7 +559,6 @@ struct ieee80211_conf { int beacon_int; u32 flags; int power_level; - int user_power_level; u16 listen_interval; bool radio_enabled; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 85c4d3144f9..fa5ca14517f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -705,6 +705,8 @@ struct ieee80211_local { struct work_struct dynamic_ps_disable_work; struct timer_list dynamic_ps_timer; + int user_power_level; /* in dBm */ + #ifdef CONFIG_MAC80211_DEBUGFS struct local_debugfsdentries { struct dentry *rcdir; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index dca4b7da6ca..b55b9970dc9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -214,10 +214,10 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) changed |= IEEE80211_CONF_CHANGE_CHANNEL; } - if (!local->hw.conf.user_power_level) + if (!local->user_power_level) power = chan->max_power; else - power = min(chan->max_power, local->hw.conf.user_power_level); + power = min(chan->max_power, local->user_power_level); if (local->hw.conf.power_level != power) { changed |= IEEE80211_CONF_CHANGE_POWER; local->hw.conf.power_level = power; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index bb2c7135a1c..5690c3d41e7 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -551,7 +551,7 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev, else /* Automatic power level setting */ new_power_level = chan->max_power; - local->hw.conf.user_power_level = new_power_level; + local->user_power_level = new_power_level; if (local->hw.conf.power_level != new_power_level) reconf_flags |= IEEE80211_CONF_CHANGE_POWER; -- cgit v1.2.3-70-g09d2 From 4797938c5dfa22af30fd16679192972f878419a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Jan 2009 10:13:27 +0100 Subject: mac80211: clean up channel type config The channel_type really doesn't need to be the only member in a new structure, so remove the struct. Additionally, remove the _CONF_CHANGE_HT flag and use _CONF_CHANGE_CHANNEL when the channel type changes, since that's enough of a change to require reprogramming the hardware anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 5 ++--- include/net/mac80211.h | 20 +++++++------------- net/mac80211/ht.c | 8 +++++--- net/mac80211/main.c | 4 ++-- net/mac80211/mlme.c | 2 +- 5 files changed, 17 insertions(+), 22 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 8929b02aa22..5e9a3e19da4 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -2117,8 +2117,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ieee80211_conf *conf = &hw->conf; mutex_lock(&sc->mutex); - if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | - IEEE80211_CONF_CHANGE_HT)) { + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos; @@ -2144,7 +2143,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) sc->sc_ah->ah_channels[pos].chanmode = ath_get_extchanmode(sc, curchan, - conf->ht.channel_type); + conf->channel_type); } ath_update_chainmask(sc, conf_is_ht(conf)); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0ffe932942f..76537f10387 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -507,10 +507,6 @@ static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) } #define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME()) -struct ieee80211_ht_conf { - enum nl80211_channel_type channel_type; -}; - /** * enum ieee80211_conf_changed - denotes which configuration changed * @@ -520,9 +516,8 @@ struct ieee80211_ht_conf { * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed * @IEEE80211_CONF_CHANGE_PS: the PS flag changed * @IEEE80211_CONF_CHANGE_POWER: the TX power changed - * @IEEE80211_CONF_CHANGE_CHANNEL: the channel changed + * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed - * @IEEE80211_CONF_CHANGE_HT: HT configuration changed */ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0), @@ -533,7 +528,6 @@ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_POWER = BIT(5), IEEE80211_CONF_CHANGE_CHANNEL = BIT(6), IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), - IEEE80211_CONF_CHANGE_HT = BIT(8), }; /** @@ -547,7 +541,7 @@ enum ieee80211_conf_changed { * @flags: configuration flags defined above * @power_level: requested transmit power (in dBm) * @channel: the channel to tune to - * @ht: the HT configuration for the device + * @channel_type: the channel (HT) type * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, * but actually means the number of transmissions not the number of retries @@ -566,7 +560,7 @@ struct ieee80211_conf { u8 long_frame_max_tx_count, short_frame_max_tx_count; struct ieee80211_channel *channel; - struct ieee80211_ht_conf ht; + enum nl80211_channel_type channel_type; }; /** @@ -1960,19 +1954,19 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops); static inline bool conf_is_ht20(struct ieee80211_conf *conf) { - return conf->ht.channel_type == NL80211_CHAN_HT20; + return conf->channel_type == NL80211_CHAN_HT20; } static inline bool conf_is_ht40_minus(struct ieee80211_conf *conf) { - return conf->ht.channel_type == NL80211_CHAN_HT40MINUS; + return conf->channel_type == NL80211_CHAN_HT40MINUS; } static inline bool conf_is_ht40_plus(struct ieee80211_conf *conf) { - return conf->ht.channel_type == NL80211_CHAN_HT40PLUS; + return conf->channel_type == NL80211_CHAN_HT40PLUS; } static inline bool @@ -1984,7 +1978,7 @@ conf_is_ht40(struct ieee80211_conf *conf) static inline bool conf_is_ht(struct ieee80211_conf *conf) { - return conf->ht.channel_type != NL80211_CHAN_NO_HT; + return conf->channel_type != NL80211_CHAN_NO_HT; } #endif /* MAC80211_H */ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index f6547de5ac6..832adf888ac 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -131,12 +131,14 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, } ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || - channel_type != local->hw.conf.ht.channel_type; + channel_type != local->hw.conf.channel_type; local->oper_channel_type = channel_type; - if (ht_changed) - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT); + if (ht_changed) { + /* channel_type change automatically detected */ + ieee80211_hw_config(local, 0); + } /* disable HT */ if (!enable_ht) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b55b9970dc9..e9f3e85d1a9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -208,9 +208,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) } if (chan != local->hw.conf.channel || - channel_type != local->hw.conf.ht.channel_type) { + channel_type != local->hw.conf.channel_type) { local->hw.conf.channel = chan; - local->hw.conf.ht.channel_type = channel_type; + local->hw.conf.channel_type = channel_type; changed |= IEEE80211_CONF_CHANGE_CHANNEL; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aafa112ae09..6a90171c859 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -901,8 +901,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); + /* channel(_type) changes are handled by ieee80211_hw_config */ local->oper_channel_type = NL80211_CHAN_NO_HT; - config_changed |= IEEE80211_CONF_CHANGE_HT; del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); -- cgit v1.2.3-70-g09d2 From 46f2c4bd7e2ba2cfedbcd4fe15d316eebc608cba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 6 Jan 2009 18:13:18 +0100 Subject: mac80211: move dynamic PS timeout to hardware config This will be needed for drivers that set the IEEE80211_HW_NO_STACK_DYNAMIC_PS flag and still want to handle dynamic PS. Signed-off-by: Johannes Berg Reviewed-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/mac80211.h | 11 +++++++---- net/mac80211/ieee80211_i.h | 1 - net/mac80211/mlme.c | 5 +++-- net/mac80211/tx.c | 4 ++-- net/mac80211/wext.c | 14 ++++++++------ 5 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 76537f10387..83ee8a21229 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -515,6 +515,7 @@ static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed * @IEEE80211_CONF_CHANGE_PS: the PS flag changed + * @IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT: the dynamic PS timeout changed * @IEEE80211_CONF_CHANGE_POWER: the TX power changed * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed @@ -525,9 +526,10 @@ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3), IEEE80211_CONF_CHANGE_PS = BIT(4), - IEEE80211_CONF_CHANGE_POWER = BIT(5), - IEEE80211_CONF_CHANGE_CHANNEL = BIT(6), - IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), + IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT = BIT(5), + IEEE80211_CONF_CHANGE_POWER = BIT(6), + IEEE80211_CONF_CHANGE_CHANNEL = BIT(7), + IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(8), }; /** @@ -540,6 +542,7 @@ enum ieee80211_conf_changed { * @listen_interval: listen interval in units of beacon interval * @flags: configuration flags defined above * @power_level: requested transmit power (in dBm) + * @dynamic_ps_timeout: dynamic powersave timeout (in ms) * @channel: the channel to tune to * @channel_type: the channel (HT) type * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame @@ -552,7 +555,7 @@ enum ieee80211_conf_changed { struct ieee80211_conf { int beacon_int; u32 flags; - int power_level; + int power_level, dynamic_ps_timeout; u16 listen_interval; bool radio_enabled; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fa5ca14517f..3db6bc3cdaf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -700,7 +700,6 @@ struct ieee80211_local { unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ bool powersave; - int dynamic_ps_timeout; struct work_struct dynamic_ps_enable_work; struct work_struct dynamic_ps_disable_work; struct timer_list dynamic_ps_timer; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6a90171c859..7709e764567 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -777,9 +777,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, if (local->powersave && !(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { - if (local->dynamic_ps_timeout > 0) + if (local->hw.conf.dynamic_ps_timeout > 0) mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(local->dynamic_ps_timeout)); + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); else { ieee80211_send_nullfunc(local, sdata, 1); conf->flags |= IEEE80211_CONF_PS; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 96eca341160..b18a7269011 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1296,7 +1296,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) } if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && - local->dynamic_ps_timeout > 0) { + local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); @@ -1305,7 +1305,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) } mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(local->dynamic_ps_timeout)); + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); } memset(info, 0, sizeof(*info)); diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 3fc1b903bfb..3f2db0bda46 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -863,17 +863,19 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, timeout = wrq->value / 1000; set: - if (ps == local->powersave && timeout == local->dynamic_ps_timeout) + if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) return ret; local->powersave = ps; - local->dynamic_ps_timeout = timeout; + conf->dynamic_ps_timeout = timeout; - if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && - (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)) { - if (local->dynamic_ps_timeout > 0) + if (local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) { + ret = ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); + } else if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { + if (conf->dynamic_ps_timeout > 0) mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(local->dynamic_ps_timeout)); + msecs_to_jiffies(conf->dynamic_ps_timeout)); else { if (local->powersave) { ieee80211_send_nullfunc(local, sdata, 1); -- cgit v1.2.3-70-g09d2 From 4be8c3873e0b88397866d3ede578503e188f9ad2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Jan 2009 18:28:20 +0100 Subject: mac80211: extend/document powersave API This modifies hardware flags for powersave to support three different flags: * IEEE80211_HW_SUPPORTS_PS - indicates general PS support * IEEE80211_HW_PS_NULLFUNC_STACK - indicates nullfunc sending in software * IEEE80211_HW_SUPPORTS_DYNAMIC_PS - indicates dynamic PS on the device It also adds documentation for all this which explains how to set the various flags. Additionally, it fixes a few things: * a spot where && was used to test flags * enable CONF_PS only when associated again Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 8 +++-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 +- drivers/net/wireless/rt2x00/rt2400pci.c | 4 ++- drivers/net/wireless/rt2x00/rt2500pci.c | 4 ++- drivers/net/wireless/rt2x00/rt2500usb.c | 4 ++- drivers/net/wireless/rt2x00/rt61pci.c | 4 ++- drivers/net/wireless/rt2x00/rt73usb.c | 4 ++- include/net/mac80211.h | 53 +++++++++++++++++++++++++++++---- net/mac80211/mlme.c | 31 ++++++++++--------- net/mac80211/tx.c | 2 +- net/mac80211/wext.c | 40 +++++++++++++++---------- 11 files changed, 111 insertions(+), 46 deletions(-) (limited to 'include/net') diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index bdf908a6e54..8af6d962687 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -17,8 +17,7 @@ - 2007 - 2008 + 2007-2009 Johannes Berg @@ -223,6 +222,11 @@ usage should require reading the full document. !Finclude/net/mac80211.h ieee80211_key_flags + + Powersave support +!Pinclude/net/mac80211.h Powersave support + + Multiple queues and QoS support TBD diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 76315c30e6f..07c3870365b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -805,7 +805,8 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | - IEEE80211_HW_AMPDU_AGGREGATION; + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SUPPORTS_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 9104113270d..ae8bfd6b59d 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1449,7 +1449,9 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index ebcc4977092..bca6798be15 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1749,7 +1749,9 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index e992bad6464..27a6971df57 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1801,7 +1801,9 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 82d35a5a4aa..549480a963e 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2556,7 +2556,9 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 2b70c01b55e..849220236c7 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2077,7 +2077,9 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 83ee8a21229..8a305bfdb87 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -850,10 +850,15 @@ enum ieee80211_tkip_key_type { * @IEEE80211_HW_AMPDU_AGGREGATION: * Hardware supports 11n A-MPDU aggregation. * - * @IEEE80211_HW_NO_STACK_DYNAMIC_PS: - * Hardware which has dynamic power save support, meaning - * that power save is enabled in idle periods, and don't need support - * from stack. + * @IEEE80211_HW_SUPPORTS_PS: + * Hardware has power save support (i.e. can go to sleep). + * + * @IEEE80211_HW_PS_NULLFUNC_STACK: + * Hardware requires nullfunc frame handling in stack, implies + * stack support for dynamic PS. + * + * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS: + * Hardware has support for dynamic PS. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -866,7 +871,9 @@ enum ieee80211_hw_flags { IEEE80211_HW_NOISE_DBM = 1<<8, IEEE80211_HW_SPECTRUM_MGMT = 1<<9, IEEE80211_HW_AMPDU_AGGREGATION = 1<<10, - IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11, + IEEE80211_HW_SUPPORTS_PS = 1<<11, + IEEE80211_HW_PS_NULLFUNC_STACK = 1<<12, + IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<13, }; /** @@ -1052,6 +1059,42 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * handler is software decryption with wrap around of iv16. */ +/** + * DOC: Powersave support + * + * mac80211 has support for various powersave implementations. + * + * First, it can support hardware that handles all powersaving by + * itself, such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS + * hardware flag. In that case, it will be told about the desired + * powersave mode depending on the association status, and the driver + * must take care of sending nullfunc frames when necessary, i.e. when + * entering and leaving powersave mode. The driver is required to look at + * the AID in beacons and signal to the AP that it woke up when it finds + * traffic directed to it. This mode supports dynamic PS by simply + * enabling/disabling PS. + * + * Additionally, such hardware may set the %IEEE80211_HW_SUPPORTS_DYNAMIC_PS + * flag to indicate that it can support dynamic PS mode itself (see below). + * + * Other hardware designs cannot send nullfunc frames by themselves and also + * need software support for parsing the TIM bitmap. This is also supported + * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and + * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still + * required to pass up beacons. Additionally, in this case, mac80211 will + * wake up the hardware when multicast traffic is announced in the beacon. + * + * FIXME: I don't think we can be fast enough in software when we want to + * receive multicast traffic? + * + * Dynamic powersave mode is an extension to normal powersave mode in which + * the hardware stays awake for a user-specified period of time after sending + * a frame so that reply frames need not be buffered and therefore delayed + * to the next wakeup. This can either be supported by hardware, in which case + * the driver needs to look at the @dynamic_ps_timeout hardware configuration + * value, or by the stack if all nullfunc handling is in the stack. + */ + /** * DOC: Frame filtering * diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7709e764567..a1e683e305f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -775,17 +775,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_BASIC_RATES; ieee80211_bss_info_change_notify(sdata, bss_info_changed); - if (local->powersave && - !(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { - if (local->hw.conf.dynamic_ps_timeout > 0) + if (local->powersave) { + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) && + local->hw.conf.dynamic_ps_timeout > 0) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); - else { - ieee80211_send_nullfunc(local, sdata, 1); + } else { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + ieee80211_send_nullfunc(local, sdata, 1); conf->flags |= IEEE80211_CONF_PS; - ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } } @@ -1779,16 +1779,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, elems.wmm_param_len); - if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK && + local->hw.conf.flags & IEEE80211_CONF_PS) { directed_tim = check_tim(&elems, ifsta->aid, &is_mc); if (directed_tim || is_mc) { - if (local->hw.conf.flags && IEEE80211_CONF_PS) { - local->hw.conf.flags &= ~IEEE80211_CONF_PS; - ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); - ieee80211_send_nullfunc(local, sdata, 0); - } + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + ieee80211_send_nullfunc(local, sdata, 0); } } @@ -2694,9 +2692,10 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (local->hw.conf.flags & IEEE80211_CONF_PS) return; - ieee80211_send_nullfunc(local, sdata, 1); - local->hw.conf.flags |= IEEE80211_CONF_PS; + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + ieee80211_send_nullfunc(local, sdata, 1); + local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b18a7269011..cd6bc87eec7 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1295,7 +1295,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } - if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && + if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 3f2db0bda46..1e5b29bdb3a 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -837,6 +837,9 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, int ret = 0, timeout = 0; bool ps; + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) + return -EOPNOTSUPP; + if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; @@ -862,32 +865,37 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, if (wrq->flags & IW_POWER_TIMEOUT) timeout = wrq->value / 1000; -set: + set: if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) return ret; local->powersave = ps; conf->dynamic_ps_timeout = timeout; - if (local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) { + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); - } else if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { - if (conf->dynamic_ps_timeout > 0) - mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(conf->dynamic_ps_timeout)); - else { - if (local->powersave) { + + if (!(sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)) + return ret; + + if (conf->dynamic_ps_timeout > 0 && + !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) { + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(conf->dynamic_ps_timeout)); + } else { + if (local->powersave) { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) ieee80211_send_nullfunc(local, sdata, 1); - conf->flags |= IEEE80211_CONF_PS; - ret = ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); - } else { - conf->flags &= ~IEEE80211_CONF_PS; - ret = ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); + conf->flags |= IEEE80211_CONF_PS; + ret = ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); + } else { + conf->flags &= ~IEEE80211_CONF_PS; + ret = ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) ieee80211_send_nullfunc(local, sdata, 0); - } } } -- cgit v1.2.3-70-g09d2 From 3e0c3ff36c4c7b9e39af7d600e399664ca04e817 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 7 Jan 2009 17:43:34 -0800 Subject: cfg80211: allow multiple driver regulatory_hints() We add support for multiple drivers to provide a regulatory_hint() on a system by adding a wiphy specific regulatory domain cache. This allows drivers to keep around cache their own regulatory domain structure queried from CRDA. We handle conflicts by intersecting multiple regulatory domains, each driver will stick to its own regulatory domain though unless a country IE has been received and processed. If the user already requested a regulatory domain and a driver requests the same regulatory domain then simply copy to the driver's regd the same regulatory domain and do not call CRDA, do not collect $200. Signed-off-by: Luis R. Rodriguez Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/wireless.h | 6 +++ net/wireless/reg.c | 104 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/wireless.h b/include/net/wireless.h index 21c5d966142..9e73aae40c5 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -187,6 +187,10 @@ struct ieee80211_supported_band { * we will disregard the first regulatory hint (when the * initiator is %REGDOM_SET_BY_CORE). * @reg_notifier: the driver's regulatory notification callback + * @regd: the driver's regulatory domain, if one was requested via + * the regulatory_hint() API. This can be used by the driver + * on the reg_notifier() if it chooses to ignore future + * regulatory domain changes caused by other drivers. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -213,6 +217,8 @@ struct wiphy { /* fields below are read-only, assigned by cfg80211 */ + const struct ieee80211_regdomain *regd; + /* the item in /sys/class/ieee80211/ points to this, * you need use set_wiphy_dev() (see below) */ struct device dev; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0f93d4526f3..5a746cd114a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -784,6 +784,7 @@ static u32 map_regdom_flags(u32 rd_flags) /** * freq_reg_info - get regulatory information for the given frequency + * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one * you can set this to 0. If this frequency is allowed we then set @@ -802,22 +803,31 @@ static u32 map_regdom_flags(u32 rd_flags) * freq_in_rule_band() for our current definition of a band -- this is purely * subjective and right now its 802.11 specific. */ -static int freq_reg_info(u32 center_freq, u32 *bandwidth, +static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, const struct ieee80211_reg_rule **reg_rule) { int i; bool band_rule_found = false; + const struct ieee80211_regdomain *regd; u32 max_bandwidth = 0; - if (!cfg80211_regdomain) + regd = cfg80211_regdomain; + + /* Follow the driver's regulatory domain, if present, unless a country + * IE has been processed */ + if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && + wiphy->regd) + regd = wiphy->regd; + + if (!regd) return -EINVAL; - for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; const struct ieee80211_power_rule *pr = NULL; - rr = &cfg80211_regdomain->reg_rules[i]; + rr = ®d->reg_rules[i]; fr = &rr->freq_range; pr = &rr->power_rule; @@ -859,7 +869,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, flags = chan->orig_flags; - r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), + r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), &max_bandwidth, ®_rule); if (r) { @@ -952,6 +962,30 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) wiphy->reg_notifier(wiphy, setby); } +static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, + const struct ieee80211_regdomain *src_regd) +{ + struct ieee80211_regdomain *regd; + int size_of_regd = 0; + unsigned int i; + + size_of_regd = sizeof(struct ieee80211_regdomain) + + ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); + + regd = kzalloc(size_of_regd, GFP_KERNEL); + if (!regd) + return -ENOMEM; + + memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); + + for (i = 0; i < src_regd->n_reg_rules; i++) + memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], + sizeof(struct ieee80211_reg_rule)); + + *dst_regd = regd; + return 0; +} + /* Return value which can be used by ignore_request() to indicate * it has been determined we should intersect two regulatory domains */ #define REG_INTERSECT 1 @@ -999,9 +1033,9 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, } return REG_INTERSECT; case REGDOM_SET_BY_DRIVER: - if (last_request->initiator == REGDOM_SET_BY_DRIVER) - return -EALREADY; - return 0; + if (last_request->initiator == REGDOM_SET_BY_CORE) + return 0; + return REG_INTERSECT; case REGDOM_SET_BY_USER: if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) return REG_INTERSECT; @@ -1028,11 +1062,28 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, r = ignore_request(wiphy, set_by, alpha2); - if (r == REG_INTERSECT) + if (r == REG_INTERSECT) { + if (set_by == REGDOM_SET_BY_DRIVER) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) + return r; + } intersect = true; - else if (r) + } else if (r) { + /* If the regulatory domain being requested by the + * driver has already been set just copy it to the + * wiphy */ + if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) { + r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); + if (r) + return r; + r = -EALREADY; + goto new_request; + } return r; + } +new_request: request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -1048,6 +1099,11 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, kfree(last_request); last_request = request; + + /* When r == REG_INTERSECT we do need to call CRDA */ + if (r < 0) + return r; + /* * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled * AND if CRDA is NOT present nothing will happen, if someone @@ -1341,6 +1397,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) } if (!last_request->intersect) { + int r; + + if (last_request->initiator != REGDOM_SET_BY_DRIVER) { + reset_regdomains(); + cfg80211_regdomain = rd; + return 0; + } + + /* For a driver hint, lets copy the regulatory domain the + * driver wanted to the wiphy to deal with conflicts */ + + BUG_ON(last_request->wiphy->regd); + + r = reg_copy_regd(&last_request->wiphy->regd, rd); + if (r) + return r; + reset_regdomains(); cfg80211_regdomain = rd; return 0; @@ -1354,8 +1427,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!intersected_rd) return -EINVAL; - /* We can trash what CRDA provided now */ - kfree(rd); + /* We can trash what CRDA provided now. + * However if a driver requested this specific regulatory + * domain we keep it for its private use */ + if (last_request->initiator == REGDOM_SET_BY_DRIVER) + last_request->wiphy->regd = rd; + else + kfree(rd); + rd = NULL; reset_regdomains(); @@ -1439,6 +1518,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) /* Caller must hold cfg80211_drv_mutex */ void reg_device_remove(struct wiphy *wiphy) { + kfree(wiphy->regd); if (!last_request || !last_request->wiphy) return; if (last_request->wiphy != wiphy) -- cgit v1.2.3-70-g09d2 From 5394af4d86ae51b369ff243c3f75b6f9a74e164b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:31:59 +0200 Subject: mac80211: 802.11w - STA flag for MFP Add flags for setting STA entries and struct ieee80211_if_sta to indicate whether management frame protection (MFP) is used. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 2 ++ net/mac80211/cfg.c | 4 ++++ net/mac80211/debugfs_sta.c | 5 +++-- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 7 +++++-- net/mac80211/sta_info.h | 2 ++ 7 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e86ed59f9ad..218f0e73a7a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -412,12 +412,14 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, NL80211_STA_FLAG_AUTHORIZED, NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 23c0ab74ded..6619ed10613 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -112,12 +112,14 @@ struct beacon_parameters { * @STATION_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short preambles * @STATION_FLAG_WME: station is WME/QoS capable + * @STATION_FLAG_MFP: station uses management frame protection */ enum station_flags { STATION_FLAG_CHANGED = 1<<0, STATION_FLAG_AUTHORIZED = 1<flags &= ~WLAN_STA_WME; if (params->station_flags & STATION_FLAG_WME) sta->flags |= WLAN_STA_WME; + + sta->flags &= ~WLAN_STA_MFP; + if (params->station_flags & STATION_FLAG_MFP) + sta->flags |= WLAN_STA_MFP; spin_unlock_bh(&sta->lock); } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a2fbe013131..90230c718b5 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -67,14 +67,15 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, char buf[100]; struct sta_info *sta = file->private_data; u32 staflags = get_sta_flags(sta); - int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s", + int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s", staflags & WLAN_STA_AUTH ? "AUTH\n" : "", staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "", staflags & WLAN_STA_PS ? "PS\n" : "", staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", staflags & WLAN_STA_WME ? "WME\n" : "", - staflags & WLAN_STA_WDS ? "WDS\n" : ""); + staflags & WLAN_STA_WDS ? "WDS\n" : "", + staflags & WLAN_STA_MFP ? "MFP\n" : ""); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3db6bc3cdaf..b5f86cb1763 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -260,6 +260,7 @@ struct mesh_preq_queue { #define IEEE80211_STA_PRIVACY_INVOKED BIT(13) #define IEEE80211_STA_TKIP_WEP_USED BIT(14) #define IEEE80211_STA_CSA_RECEIVED BIT(15) +#define IEEE80211_STA_MFP_ENABLED BIT(16) /* flags for MLME request */ #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_DIRECT_PROBE 1 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a1e683e305f..bc8a7f1a6a1 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1,6 +1,6 @@ /* * BSS client mode implementation - * Copyright 2003, Jouni Malinen + * Copyright 2003-2008, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc @@ -472,7 +472,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - ieee80211_tx_skb(sdata, skb, 0); + ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); } /* MLME */ @@ -1408,6 +1408,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, rate_control_rate_init(sta); + if (ifsta->flags & IEEE80211_STA_MFP_ENABLED) + set_sta_flags(sta, WLAN_STA_MFP); + if (elems.wmm_param) set_sta_flags(sta, WLAN_STA_WME); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b683d3f5ef8..d13a44b935e 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -34,6 +34,7 @@ * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next * frame to this station is transmitted. + * @WLAN_STA_MFP: Management frame protection is used with this STA. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -46,6 +47,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_WDS = 1<<7, WLAN_STA_PSPOLL = 1<<8, WLAN_STA_CLEAR_PS_FILT = 1<<9, + WLAN_STA_MFP = 1<<10, }; #define STA_TID_NUM 16 -- cgit v1.2.3-70-g09d2 From 3cfcf6ac6d69dc290e96416731eea5c88ac7d426 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:02 +0200 Subject: mac80211: 802.11w - Use BIP (AES-128-CMAC) Add mechanism for managing BIP keys (IGTK) and integrate BIP into the TX/RX paths. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 3 ++ include/linux/ieee80211.h | 1 + include/linux/nl80211.h | 6 ++- include/net/cfg80211.h | 5 +++ include/net/mac80211.h | 2 + net/mac80211/cfg.c | 31 ++++++++++++++ net/mac80211/debugfs_key.c | 79 ++++++++++++++++++++++++++++++++++- net/mac80211/debugfs_key.h | 10 +++++ net/mac80211/ieee80211_i.h | 5 ++- net/mac80211/key.c | 62 ++++++++++++++++++++++++++- net/mac80211/key.h | 6 +++ net/mac80211/rx.c | 90 ++++++++++++++++++++++++++++++++++++---- net/mac80211/tx.c | 9 ++++ net/wireless/nl80211.c | 29 +++++++++---- 14 files changed, 317 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 5b416ed6529..e758b70ab7e 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1026,6 +1026,9 @@ int ath5k_keycache_type(const struct ieee80211_key_conf *key) return AR5K_KEYTABLE_TYPE_40; else if (key->keylen == LEN_WEP104) return AR5K_KEYTABLE_TYPE_104; + return -EINVAL; + default: + return -EINVAL; } return -EINVAL; } diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cceb9e86c74..df98a8a549a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1139,6 +1139,7 @@ enum ieee80211_back_parties { /* reserved: 0x000FAC03 */ #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 #define WLAN_MAX_KEY_LEN 32 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 218f0e73a7a..ee742bc9761 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -72,8 +72,8 @@ * * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. - * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT or - * %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC and %NL80211_ATTR_KEY_CIPHER * attributes. @@ -346,6 +346,8 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_FREQ, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_ATTR_KEY_DEFAULT_MGMT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6619ed10613..df78abc496f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -473,6 +473,8 @@ struct ieee80211_channel; * * @set_default_key: set the default key on an interface * + * @set_default_mgmt_key: set the default management frame key on an interface + * * @add_beacon: Add a beacon with given parameters, @head, @interval * and @dtim_period will be valid, @tail is optional. * @set_beacon: Change the beacon parameters for an access point mode @@ -520,6 +522,9 @@ struct cfg80211_ops { int (*set_default_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index); + int (*set_default_mgmt_key)(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index); int (*add_beacon)(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8a305bfdb87..61f1f37a9e2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -651,11 +651,13 @@ struct ieee80211_if_conf { * @ALG_WEP: WEP40 or WEP104 * @ALG_TKIP: TKIP * @ALG_CCMP: CCMP (AES) + * @ALG_AES_CMAC: AES-128-CMAC */ enum ieee80211_key_alg { ALG_WEP, ALG_TKIP, ALG_CCMP, + ALG_AES_CMAC, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 309d9189aa4..72c10691543 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -133,6 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case WLAN_CIPHER_SUITE_CCMP: alg = ALG_CCMP; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + alg = ALG_AES_CMAC; + break; default: return -EINVAL; } @@ -275,6 +278,17 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, else params.cipher = WLAN_CIPHER_SUITE_WEP104; break; + case ALG_AES_CMAC: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + seq[0] = key->u.aes_cmac.tx_pn[5]; + seq[1] = key->u.aes_cmac.tx_pn[4]; + seq[2] = key->u.aes_cmac.tx_pn[3]; + seq[3] = key->u.aes_cmac.tx_pn[2]; + seq[4] = key->u.aes_cmac.tx_pn[1]; + seq[5] = key->u.aes_cmac.tx_pn[0]; + params.seq = seq; + params.seq_len = 6; + break; } params.key = key->conf.key; @@ -304,6 +318,22 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, return 0; } +static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_idx) +{ + struct ieee80211_sub_if_data *sdata; + + rcu_read_lock(); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ieee80211_set_default_mgmt_key(sdata, key_idx); + + rcu_read_unlock(); + + return 0; +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -1153,6 +1183,7 @@ struct cfg80211_ops mac80211_config_ops = { .del_key = ieee80211_del_key, .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, + .set_default_mgmt_key = ieee80211_config_default_mgmt_key, .add_beacon = ieee80211_add_beacon, .set_beacon = ieee80211_set_beacon, .del_beacon = ieee80211_del_beacon, diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 6424ac565ae..99c752588b3 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -76,6 +76,9 @@ static ssize_t key_algorithm_read(struct file *file, case ALG_CCMP: alg = "CCMP\n"; break; + case ALG_AES_CMAC: + alg = "AES-128-CMAC\n"; + break; default: return 0; } @@ -105,6 +108,12 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); break; + case ALG_AES_CMAC: + tpn = key->u.aes_cmac.tx_pn; + len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", + tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], + tpn[5]); + break; default: return 0; } @@ -142,6 +151,14 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, } len = p - buf; break; + case ALG_AES_CMAC: + rpn = key->u.aes_cmac.rx_pn; + p += scnprintf(p, sizeof(buf)+buf-p, + "%02x%02x%02x%02x%02x%02x\n", + rpn[0], rpn[1], rpn[2], + rpn[3], rpn[4], rpn[5]); + len = p - buf; + break; default: return 0; } @@ -156,13 +173,40 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf, char buf[20]; int len; - if (key->conf.alg != ALG_CCMP) + switch (key->conf.alg) { + case ALG_CCMP: + len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); + break; + case ALG_AES_CMAC: + len = scnprintf(buf, sizeof(buf), "%u\n", + key->u.aes_cmac.replays); + break; + default: return 0; - len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); + } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(replays); +static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + char buf[20]; + int len; + + switch (key->conf.alg) { + case ALG_AES_CMAC: + len = scnprintf(buf, sizeof(buf), "%u\n", + key->u.aes_cmac.icverrors); + break; + default: + return 0; + } + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(icverrors); + static ssize_t key_key_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -222,6 +266,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) DEBUGFS_ADD(tx_spec); DEBUGFS_ADD(rx_spec); DEBUGFS_ADD(replays); + DEBUGFS_ADD(icverrors); DEBUGFS_ADD(key); DEBUGFS_ADD(ifindex); }; @@ -243,6 +288,7 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key) DEBUGFS_DEL(tx_spec); DEBUGFS_DEL(rx_spec); DEBUGFS_DEL(replays); + DEBUGFS_DEL(icverrors); DEBUGFS_DEL(key); DEBUGFS_DEL(ifindex); @@ -280,6 +326,35 @@ void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) sdata->common_debugfs.default_key = NULL; } +void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) +{ + char buf[50]; + struct ieee80211_key *key; + + if (!sdata->debugfsdir) + return; + + /* this is running under the key lock */ + + key = sdata->default_mgmt_key; + if (key) { + sprintf(buf, "../keys/%d", key->debugfs.cnt); + sdata->common_debugfs.default_mgmt_key = + debugfs_create_symlink("default_mgmt_key", + sdata->debugfsdir, buf); + } else + ieee80211_debugfs_key_remove_mgmt_default(sdata); +} + +void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) +{ + if (!sdata) + return; + + debugfs_remove(sdata->common_debugfs.default_mgmt_key); + sdata->common_debugfs.default_mgmt_key = NULL; +} + void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) { diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h index b1a3754ee24..54717b4e137 100644 --- a/net/mac80211/debugfs_key.h +++ b/net/mac80211/debugfs_key.h @@ -6,6 +6,10 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key); void ieee80211_debugfs_key_remove(struct ieee80211_key *key); void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_add_mgmt_default( + struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_remove_mgmt_default( + struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta); #else @@ -19,6 +23,12 @@ static inline void ieee80211_debugfs_key_add_default( static inline void ieee80211_debugfs_key_remove_default( struct ieee80211_sub_if_data *sdata) {} +static inline void ieee80211_debugfs_key_add_mgmt_default( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_key_remove_mgmt_default( + struct ieee80211_sub_if_data *sdata) +{} static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) {} diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 20af92abd61..8c3245717c5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -409,8 +409,10 @@ struct ieee80211_sub_if_data { unsigned int fragment_next; #define NUM_DEFAULT_KEYS 4 - struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; +#define NUM_DEFAULT_MGMT_KEYS 2 + struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key *default_key; + struct ieee80211_key *default_mgmt_key; u16 sequence_number; @@ -482,6 +484,7 @@ struct ieee80211_sub_if_data { } debugfs; struct { struct dentry *default_key; + struct dentry *default_mgmt_key; } common_debugfs; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/key.c b/net/mac80211/key.c index b0a025c9b61..19b480de4bb 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -18,6 +18,7 @@ #include "ieee80211_i.h" #include "debugfs_key.h" #include "aes_ccm.h" +#include "aes_cmac.h" /** @@ -215,13 +216,38 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) spin_unlock_irqrestore(&sdata->local->key_lock, flags); } +static void +__ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) +{ + struct ieee80211_key *key = NULL; + + if (idx >= NUM_DEFAULT_KEYS && + idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) + key = sdata->keys[idx]; + + rcu_assign_pointer(sdata->default_mgmt_key, key); + + if (key) + add_todo(key, KEY_FLAG_TODO_DEFMGMTKEY); +} + +void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, + int idx) +{ + unsigned long flags; + + spin_lock_irqsave(&sdata->local->key_lock, flags); + __ieee80211_set_default_mgmt_key(sdata, idx); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); +} + static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_key *old, struct ieee80211_key *new) { - int idx, defkey; + int idx, defkey, defmgmtkey; if (new) list_add(&new->list, &sdata->key_list); @@ -237,13 +263,19 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, idx = new->conf.keyidx; defkey = old && sdata->default_key == old; + defmgmtkey = old && sdata->default_mgmt_key == old; if (defkey && !new) __ieee80211_set_default_key(sdata, -1); + if (defmgmtkey && !new) + __ieee80211_set_default_mgmt_key(sdata, -1); rcu_assign_pointer(sdata->keys[idx], new); if (defkey && new) __ieee80211_set_default_key(sdata, new->conf.keyidx); + if (defmgmtkey && new) + __ieee80211_set_default_mgmt_key(sdata, + new->conf.keyidx); } if (old) { @@ -262,7 +294,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, { struct ieee80211_key *key; - BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); + BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); if (!key) @@ -291,6 +323,10 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; break; + case ALG_AES_CMAC: + key->conf.iv_len = 0; + key->conf.icv_len = sizeof(struct ieee80211_mmie); + break; } memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); @@ -308,6 +344,19 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, } } + if (alg == ALG_AES_CMAC) { + /* + * Initialize AES key state here as an optimization so that + * it does not need to be initialized for every packet. + */ + key->u.aes_cmac.tfm = + ieee80211_aes_cmac_key_setup(key_data); + if (!key->u.aes_cmac.tfm) { + kfree(key); + return NULL; + } + } + return key; } @@ -461,6 +510,8 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) if (key->conf.alg == ALG_CCMP) ieee80211_aes_key_free(key->u.ccmp.tfm); + if (key->conf.alg == ALG_AES_CMAC) + ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); ieee80211_debugfs_key_remove(key); kfree(key); @@ -483,6 +534,7 @@ static void __ieee80211_key_todo(void) list_del_init(&key->todo); todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | KEY_FLAG_TODO_DEFKEY | + KEY_FLAG_TODO_DEFMGMTKEY | KEY_FLAG_TODO_HWACCEL_ADD | KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); @@ -500,6 +552,11 @@ static void __ieee80211_key_todo(void) ieee80211_debugfs_key_add_default(key->sdata); work_done = true; } + if (todoflags & KEY_FLAG_TODO_DEFMGMTKEY) { + ieee80211_debugfs_key_remove_mgmt_default(key->sdata); + ieee80211_debugfs_key_add_mgmt_default(key->sdata); + work_done = true; + } if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { ieee80211_key_enable_hw_accel(key); work_done = true; @@ -535,6 +592,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) ieee80211_key_lock(); ieee80211_debugfs_key_remove_default(sdata); + ieee80211_debugfs_key_remove_mgmt_default(sdata); spin_lock_irqsave(&sdata->local->key_lock, flags); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 73ac28ca2ed..215d3ef42a4 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -46,6 +46,8 @@ struct sta_info; * acceleration. * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated. * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs. + * @KEY_FLAG_TODO_DEFMGMTKEY: Key is default management key and debugfs needs + * to be updated. */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), @@ -54,6 +56,7 @@ enum ieee80211_internal_key_flags { KEY_FLAG_TODO_HWACCEL_REMOVE = BIT(3), KEY_FLAG_TODO_DEFKEY = BIT(4), KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5), + KEY_FLAG_TODO_DEFMGMTKEY = BIT(6), }; struct tkip_ctx { @@ -124,6 +127,7 @@ struct ieee80211_key { struct dentry *tx_spec; struct dentry *rx_spec; struct dentry *replays; + struct dentry *icverrors; struct dentry *key; struct dentry *ifindex; int cnt; @@ -150,6 +154,8 @@ void ieee80211_key_link(struct ieee80211_key *key, struct sta_info *sta); void ieee80211_key_free(struct ieee80211_key *key); void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx); +void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, + int idx); void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b68e082e99c..abc3aa583ca 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -446,6 +446,52 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) return RX_CONTINUE; } + +static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) + return 0; + + return ieee80211_is_robust_mgmt_frame(hdr); +} + + +static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) + return 0; + + return ieee80211_is_robust_mgmt_frame(hdr); +} + + +/* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ +static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) +{ + struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; + struct ieee80211_mmie *mmie; + + if (skb->len < 24 + sizeof(*mmie) || + !is_multicast_ether_addr(hdr->da)) + return -1; + + if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) + return -1; /* not a robust management frame */ + + mmie = (struct ieee80211_mmie *) + (skb->data + skb->len - sizeof(*mmie)); + if (mmie->element_id != WLAN_EID_MMIE || + mmie->length != sizeof(*mmie) - 2) + return -1; + + return le16_to_cpu(mmie->key_id); +} + + static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { @@ -561,21 +607,23 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) int hdrlen; ieee80211_rx_result result = RX_DROP_UNUSABLE; struct ieee80211_key *stakey = NULL; + int mmie_keyidx = -1; /* * Key selection 101 * - * There are three types of keys: + * There are four types of keys: * - GTK (group keys) + * - IGTK (group keys for management frames) * - PTK (pairwise keys) * - STK (station-to-station pairwise keys) * * When selecting a key, we have to distinguish between multicast * (including broadcast) and unicast frames, the latter can only - * use PTKs and STKs while the former always use GTKs. Unless, of - * course, actual WEP keys ("pre-RSNA") are used, then unicast - * frames can also use key indizes like GTKs. Hence, if we don't - * have a PTK/STK we check the key index for a WEP key. + * use PTKs and STKs while the former always use GTKs and IGTKs. + * Unless, of course, actual WEP keys ("pre-RSNA") are used, then + * unicast frames can also use key indices like GTKs. Hence, if we + * don't have a PTK/STK we check the key index for a WEP key. * * Note that in a regular BSS, multicast frames are sent by the * AP only, associated stations unicast the frame to the AP first @@ -588,8 +636,14 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) * possible. */ - if (!ieee80211_has_protected(hdr->frame_control)) - return RX_CONTINUE; + if (!ieee80211_has_protected(hdr->frame_control)) { + if (!ieee80211_is_mgmt(hdr->frame_control) || + rx->sta == NULL || !test_sta_flags(rx->sta, WLAN_STA_MFP)) + return RX_CONTINUE; + mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); + if (mmie_keyidx < 0) + return RX_CONTINUE; + } /* * No point in finding a key and decrypting if the frame is neither @@ -603,6 +657,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (!is_multicast_ether_addr(hdr->addr1) && stakey) { rx->key = stakey; + } else if (mmie_keyidx >= 0) { + /* Broadcast/multicast robust management frame / BIP */ + if ((rx->status->flag & RX_FLAG_DECRYPTED) && + (rx->status->flag & RX_FLAG_IV_STRIPPED)) + return RX_CONTINUE; + + if (mmie_keyidx < NUM_DEFAULT_KEYS || + mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) + return RX_DROP_MONITOR; /* unexpected BIP keyidx */ + rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); } else { /* * The device doesn't give us the IV so we won't be @@ -665,6 +729,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) case ALG_CCMP: result = ieee80211_crypto_ccmp_decrypt(rx); break; + case ALG_AES_CMAC: + result = ieee80211_crypto_aes_cmac_decrypt(rx); + break; } /* either the frame has been decrypted or will be dropped */ @@ -1112,6 +1179,15 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_nullfunc(fc) && + (!ieee80211_is_mgmt(fc) || + (ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && + rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP))) && + (rx->key || rx->sdata->drop_unencrypted))) + return -EACCES; + /* BIP does not use Protected field, so need to check MMIE */ + if (unlikely(rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP) && + ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && + ieee80211_get_mmie_keyidx(rx->skb) < 0 && (rx->key || rx->sdata->drop_unencrypted))) return -EACCES; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 50c6c4fabea..ad53ea9e9c7 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -425,6 +425,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->key = NULL; else if (tx->sta && (key = rcu_dereference(tx->sta->key))) tx->key = key; + else if (ieee80211_is_mgmt(hdr->frame_control) && + (key = rcu_dereference(tx->sdata->default_mgmt_key))) + tx->key = key; else if ((key = rcu_dereference(tx->sdata->default_key))) tx->key = key; else if (tx->sdata->drop_unencrypted && @@ -453,6 +456,10 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->skb)) tx->key = NULL; break; + case ALG_AES_CMAC: + if (!ieee80211_is_mgmt(hdr->frame_control)) + tx->key = NULL; + break; } } @@ -808,6 +815,8 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) return ieee80211_crypto_tkip_encrypt(tx); case ALG_CCMP: return ieee80211_crypto_ccmp_encrypt(tx); + case ALG_AES_CMAC: + return ieee80211_crypto_aes_cmac_encrypt(tx); } /* not reached */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1e728fff474..123d3b160fa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -738,7 +738,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_KEY_IDX]) key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) @@ -804,30 +804,41 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) int err; struct net_device *dev; u8 key_idx; + int (*func)(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index); if (!info->attrs[NL80211_ATTR_KEY_IDX]) return -EINVAL; key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { + if (key_idx < 4 || key_idx > 5) + return -EINVAL; + } else if (key_idx > 3) return -EINVAL; /* currently only support setting default key */ - if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) + if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && + !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) return -EINVAL; err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) return err; - if (!drv->ops->set_default_key) { + if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) + func = drv->ops->set_default_key; + else + func = drv->ops->set_default_mgmt_key; + + if (!func) { err = -EOPNOTSUPP; goto out; } rtnl_lock(); - err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); + err = func(&drv->wiphy, dev, key_idx); rtnl_unlock(); out: @@ -863,7 +874,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; /* @@ -894,6 +905,10 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (params.key_len != 13) return -EINVAL; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + if (params.key_len != 16) + return -EINVAL; + break; default: return -EINVAL; } @@ -928,7 +943,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_KEY_IDX]) key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) -- cgit v1.2.3-70-g09d2 From 1f7d77ab69789980dad44e1af7afd3a68cd48276 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:10 +0200 Subject: mac80211: 802.11w - Optional software CCMP for management frames If driver/firmware/hardware does not support CCMP for management frames, it can now request mac80211 to take care of encrypting and decrypting management frames (when MFP is enabled) in software. The will need to add this new IEEE80211_KEY_FLAG_SW_MGMT flag when a CCMP key is being configured for TX side and return the undecrypted frames on RX side without RX_FLAG_DECRYPTED flag to use software CCMP for management frames (but hardware for data frames). Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/wpa.c | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 61f1f37a9e2..00a50a38c7f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -686,12 +686,16 @@ enum ieee80211_key_len { * generation in software. * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates * that the key is pairwise rather then a shared key. + * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a + * CCMP key if it requires CCMP encryption of management frames (MFP) to + * be done in software. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_WMM_STA = 1<<0, IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, + IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, }; /** diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 53e11e6ff66..9101b48ec2a 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -367,9 +367,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) int hdrlen, len, tail; u8 *pos, *pn; int i; + bool skip_hw; + + skip_hw = (tx->key->conf.flags & IEEE80211_KEY_FLAG_SW_MGMT) && + ieee80211_is_mgmt(hdr->frame_control); if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) && - !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { + !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) && + !skip_hw) { /* hwaccel - with no need for preallocated room for CCMP * header or MIC fields */ info->control.hw_key = &tx->key->conf; @@ -404,7 +409,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) ccmp_pn2hdr(pos, pn, key->conf.keyidx); - if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { + if ((key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) && !skip_hw) { /* hwaccel - with preallocated room for CCMP header */ info->control.hw_key = &tx->key->conf; return 0; -- cgit v1.2.3-70-g09d2 From 4375d08350e3661d5e8860d33eea084e47ba01cf Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:11 +0200 Subject: mac80211: 802.11w - Add driver capability flag for MFP This allows user space to determine whether a driver supports MFP and behave properly without having to ask user to configure this in MFP-optional mode. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/wext.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 00a50a38c7f..9bca2abbf03 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -865,6 +865,9 @@ enum ieee80211_tkip_key_type { * * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS: * Hardware has support for dynamic PS. + * + * @IEEE80211_HW_MFP_CAPABLE: + * Hardware supports management frame protection (MFP, IEEE 802.11w). */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -880,6 +883,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_PS = 1<<11, IEEE80211_HW_PS_NULLFUNC_STACK = 1<<12, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<13, + IEEE80211_HW_MFP_CAPABLE = 1<<14, }; /** diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 2dd387495df..70a29b657b6 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -976,6 +976,10 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, ret = -EOPNOTSUPP; break; case IW_AUTH_MFP: + if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) { + ret = -EOPNOTSUPP; + break; + } if (sdata->vif.type == NL80211_IFTYPE_STATION || sdata->vif.type == NL80211_IFTYPE_ADHOC) sdata->u.sta.mfp = data->value; -- cgit v1.2.3-70-g09d2 From 6dd1bf3118b62a3ce241dc2b7e05e3d4a28c9eb1 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 8 Jan 2009 21:00:34 -0500 Subject: mac80211: document return codes from ops callbacks For any callbacks in ieee80211_ops, specify what values the return codes represent. While at it, fix a couple of capitalization and punctuation differences. Signed-off-by: Bob Copeland Reviewed-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/mac80211.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9bca2abbf03..9a5869e9ecf 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1217,6 +1217,8 @@ enum ieee80211_ampdu_mlme_action { * configuration in the TX control data. This handler should, * preferably, never fail and stop queues appropriately, more * importantly, however, it must never fail for A-MPDU-queues. + * This function should return NETDEV_TX_OK except in very + * limited cases. * Must be implemented and atomic. * * @start: Called before the first netdevice attached to the hardware @@ -1257,9 +1259,12 @@ enum ieee80211_ampdu_mlme_action { * * @config: Handler for configuration requests. IEEE 802.11 code calls this * function to change hardware configuration, e.g., channel. + * This function should never fail but returns a negative error code + * if it does. * * @config_interface: Handler for configuration requests related to interfaces * (e.g. BSSID changes.) + * Returns a negative error code which will be seen in userspace. * * @bss_info_changed: Handler for configuration requests related to BSS * parameters that may vary during BSS's lifespan, and may affect low @@ -1279,6 +1284,7 @@ enum ieee80211_ampdu_mlme_action { * This callback can sleep, and is only called between add_interface * and remove_interface calls, i.e. while the given virtual interface * is enabled. + * Returns a negative error code if the key can't be added. * * @update_tkip_key: See the section "Hardware crypto acceleration" * This callback will be called in the context of Rx. Called for drivers @@ -1290,8 +1296,10 @@ enum ieee80211_ampdu_mlme_action { * bands. When the scan finishes, ieee80211_scan_completed() must be * called; note that it also must be called when the scan cannot finish * because the hardware is turned off! Anything else is a bug! + * Returns a negative error code which will be seen in userspace. * - * @get_stats: return low-level statistics + * @get_stats: Return low-level statistics. + * Returns zero if statistics are available. * * @get_tkip_seq: If your device implements TKIP encryption in hardware this * callback should be provided to read the TKIP transmit IVs (both IV32 @@ -1305,6 +1313,7 @@ enum ieee80211_ampdu_mlme_action { * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. + * Returns a negative error code on failure. * * @get_tx_stats: Get statistics of the current TX queue status. This is used * to get number of currently queued packets (queue length), maximum queue @@ -1324,13 +1333,15 @@ enum ieee80211_ampdu_mlme_action { * @tx_last_beacon: Determine whether the last IBSS beacon was sent by us. * This is needed only for IBSS mode and the result of this function is * used to determine whether to reply to Probe Requests. + * Returns non-zero if this device sent the last beacon. * * @ampdu_action: Perform a certain A-MPDU action * The RA/TID combination determines the destination and TID we want * the ampdu action to be performed for. The action is defined through * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) - * is the first frame we expect to perform the action on. notice + * is the first frame we expect to perform the action on. Notice * that TX/RX_STOP can pass NULL for this parameter. + * Returns a negative error code on failure. */ struct ieee80211_ops { int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); -- cgit v1.2.3-70-g09d2 From 9aed3cc124343d92be6697e9af3928bdfe8eb03e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 13 Jan 2009 16:03:29 +0200 Subject: nl80211: New command for adding extra IE(s) into management frames A new nl80211 command, NL80211_CMD_SET_MGMT_EXTRA_IE, can be used to add arbitrary IE data into the end of management frames. The interface allows extra IEs to be configured for each management frame subtype, but only some of them (ProbeReq, ProbeResp, Auth, (Re)AssocReq, Deauth, Disassoc) are currently accepted in mac80211 implementation. This makes it easier to implement IEEE 802.11 extensions like WPS and FT that add IE(s) into some management frames. In addition, this can be useful for testing and experimentation purposes. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 22 +++++++++++++ include/net/cfg80211.h | 26 +++++++++++++++ net/mac80211/cfg.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 16 +++++++++ net/mac80211/iface.c | 7 ++++ net/mac80211/mlme.c | 52 +++++++++++++++++++++++++---- net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++ 7 files changed, 246 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4e7a7986a52..76aae3d8e97 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -133,6 +133,14 @@ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -178,6 +186,8 @@ enum nl80211_commands { NL80211_CMD_GET_MESH_PARAMS, NL80211_CMD_SET_MESH_PARAMS, + NL80211_CMD_SET_MGMT_EXTRA_IE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -190,6 +200,7 @@ enum nl80211_commands { * here */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE /** * enum nl80211_attrs - nl80211 netlink attributes @@ -284,6 +295,12 @@ enum nl80211_commands { * supported interface types, each a flag attribute with the number * of the interface mode. * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -348,6 +365,9 @@ enum nl80211_attrs { NL80211_ATTR_KEY_DEFAULT_MGMT, + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -363,6 +383,8 @@ enum nl80211_attrs { #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index df78abc496f..c7da88fb15b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -433,6 +433,26 @@ struct ieee80211_txq_params { u8 aifs; }; +/** + * struct mgmt_extra_ie_params - Extra management frame IE parameters + * + * Used to add extra IE(s) into management frames. If the driver cannot add the + * requested data into all management frames of the specified subtype that are + * generated in kernel or firmware/hardware, it must reject the configuration + * call. The IE data buffer is added to the end of the specified management + * frame body after all other IEs. This addition is not applied to frames that + * are injected through a monitor interface. + * + * @subtype: Management frame subtype + * @ies: IE data buffer or %NULL to remove previous data + * @ies_len: Length of @ies in octets + */ +struct mgmt_extra_ie_params { + u8 subtype; + u8 *ies; + int ies_len; +}; + /* from net/wireless.h */ struct wiphy; @@ -501,6 +521,8 @@ struct ieee80211_channel; * @set_txq_params: Set TX queue parameters * * @set_channel: Set channel + * + * @set_mgmt_extra_ie: Set extra IE data for management frames */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -571,6 +593,10 @@ struct cfg80211_ops { int (*set_channel)(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); + + int (*set_mgmt_extra_ie)(struct wiphy *wiphy, + struct net_device *dev, + struct mgmt_extra_ie_params *params); }; /* temporary wext handlers */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 72c10691543..d1ac3ab2c51 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1175,6 +1175,87 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } +static int set_mgmt_extra_ie_sta(struct ieee80211_if_sta *ifsta, u8 subtype, + u8 *ies, size_t ies_len) +{ + switch (subtype) { + case IEEE80211_STYPE_PROBE_REQ >> 4: + kfree(ifsta->ie_probereq); + ifsta->ie_probereq = ies; + ifsta->ie_probereq_len = ies_len; + return 0; + case IEEE80211_STYPE_PROBE_RESP >> 4: + kfree(ifsta->ie_proberesp); + ifsta->ie_proberesp = ies; + ifsta->ie_proberesp_len = ies_len; + return 0; + case IEEE80211_STYPE_AUTH >> 4: + kfree(ifsta->ie_auth); + ifsta->ie_auth = ies; + ifsta->ie_auth_len = ies_len; + return 0; + case IEEE80211_STYPE_ASSOC_REQ >> 4: + kfree(ifsta->ie_assocreq); + ifsta->ie_assocreq = ies; + ifsta->ie_assocreq_len = ies_len; + return 0; + case IEEE80211_STYPE_REASSOC_REQ >> 4: + kfree(ifsta->ie_reassocreq); + ifsta->ie_reassocreq = ies; + ifsta->ie_reassocreq_len = ies_len; + return 0; + case IEEE80211_STYPE_DEAUTH >> 4: + kfree(ifsta->ie_deauth); + ifsta->ie_deauth = ies; + ifsta->ie_deauth_len = ies_len; + return 0; + case IEEE80211_STYPE_DISASSOC >> 4: + kfree(ifsta->ie_disassoc); + ifsta->ie_disassoc = ies; + ifsta->ie_disassoc_len = ies_len; + return 0; + } + + return -EOPNOTSUPP; +} + +static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, + struct net_device *dev, + struct mgmt_extra_ie_params *params) +{ + struct ieee80211_sub_if_data *sdata; + u8 *ies; + size_t ies_len; + int ret = -EOPNOTSUPP; + + if (params->ies) { + ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); + if (ies == NULL) + return -ENOMEM; + ies_len = params->ies_len; + } else { + ies = NULL; + ies_len = 0; + } + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + ret = set_mgmt_extra_ie_sta(&sdata->u.sta, params->subtype, + ies, ies_len); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + if (ret) + kfree(ies); + return ret; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1204,4 +1285,5 @@ struct cfg80211_ops mac80211_config_ops = { .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_channel = ieee80211_set_channel, + .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c9ffadb55d3..5eafd3affe2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -331,6 +331,22 @@ struct ieee80211_if_sta { u32 supp_rates_bits[IEEE80211_NUM_BANDS]; int wmm_last_param_set; + + /* Extra IE data for management frames */ + u8 *ie_probereq; + size_t ie_probereq_len; + u8 *ie_proberesp; + size_t ie_proberesp_len; + u8 *ie_auth; + size_t ie_auth_len; + u8 *ie_assocreq; + size_t ie_assocreq_len; + u8 *ie_reassocreq; + size_t ie_reassocreq_len; + u8 *ie_deauth; + size_t ie_deauth_len; + u8 *ie_disassoc; + size_t ie_disassoc_len; }; struct ieee80211_if_mesh { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5d5a029228b..8dc2c2188d9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -632,6 +632,13 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.sta.assocreq_ies); kfree(sdata->u.sta.assocresp_ies); kfree_skb(sdata->u.sta.probe_resp); + kfree(sdata->u.sta.ie_probereq); + kfree(sdata->u.sta.ie_proberesp); + kfree(sdata->u.sta.ie_auth); + kfree(sdata->u.sta.ie_assocreq); + kfree(sdata->u.sta.ie_reassocreq); + kfree(sdata->u.sta.ie_deauth); + kfree(sdata->u.sta.ie_disassoc); break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f0d42498c25..43da6227b37 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -131,6 +131,12 @@ u64 ieee80211_sta_get_rates(struct ieee80211_local *local, /* frame sending functions */ +static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) +{ + if (ies) + memcpy(skb_put(skb, ies_len), ies, ies_len); +} + /* also used by scanning code */ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u8 *ssid, size_t ssid_len) @@ -142,7 +148,8 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u8 *pos, *supp_rates, *esupp_rates = NULL; int i; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + + sdata->u.sta.ie_probereq_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -189,6 +196,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, *pos = rate->bitrate / 5; } + add_extra_ies(skb, sdata->u.sta.ie_probereq, + sdata->u.sta.ie_probereq_len); + ieee80211_tx_skb(sdata, skb, 0); } @@ -202,7 +212,8 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len); + sizeof(*mgmt) + 6 + extra_len + + sdata->u.sta.ie_auth_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", sdata->dev->name); @@ -225,6 +236,7 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); + add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len); ieee80211_tx_skb(sdata, skb, encrypt); } @@ -235,17 +247,26 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie; + u8 *pos, *ies, *ht_ie, *e_ies; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u64 rates = 0; + size_t e_ies_len; + + if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) { + e_ies = sdata->u.sta.ie_reassocreq; + e_ies_len = sdata->u.sta.ie_reassocreq_len; + } else { + e_ies = sdata->u.sta.ie_assocreq; + e_ies_len = sdata->u.sta.ie_assocreq_len; + } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifsta->extra_ie_len + - ifsta->ssid_len); + ifsta->ssid_len + e_ies_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -436,6 +457,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } + add_extra_ies(skb, e_ies, e_ies_len); + kfree(ifsta->assocreq_ies); ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL); @@ -453,8 +476,19 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta = &sdata->u.sta; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + u8 *ies; + size_t ies_len; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); + if (stype == IEEE80211_STYPE_DEAUTH) { + ies = sdata->u.sta.ie_deauth; + ies_len = sdata->u.sta.ie_deauth_len; + } else { + ies = sdata->u.sta.ie_disassoc; + ies_len = sdata->u.sta.ie_disassoc_len; + } + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + + ies_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for " "deauth/disassoc frame\n", sdata->dev->name); @@ -472,6 +506,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); + add_extra_ies(skb, ies, ies_len); + ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1473,7 +1509,8 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; union iwreq_data wrqu; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + + sdata->u.sta.ie_proberesp_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "response\n", sdata->dev->name); @@ -1556,6 +1593,9 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(pos, &bss->supp_rates[8], rates); } + add_extra_ies(skb, sdata->u.sta.ie_proberesp, + sdata->u.sta.ie_proberesp_len); + ifsta->probe_resp = skb; ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 123d3b160fa..09a5d0f1d6d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, + + [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, + [NL80211_ATTR_IE] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, }; /* message building helper */ @@ -2149,6 +2153,43 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } +static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct mgmt_extra_ie_params params; + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) + return -EINVAL; + params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); + if (params.subtype > 15) + return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ + + if (info->attrs[NL80211_ATTR_IE]) { + params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); + params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + if (drv->ops->set_mgmt_extra_ie) { + rtnl_lock(); + err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); + rtnl_unlock(); + } else + err = -EOPNOTSUPP; + + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2310,6 +2351,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, + .doit = nl80211_set_mgmt_extra_ie, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; /* multicast groups */ -- cgit v1.2.3-70-g09d2 From 0378b3f1c49d48ed524eabda7e4340163d9483c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 Jan 2009 11:20:52 -0500 Subject: cfg80211: add PM hooks This should help implement suspend/resume in mac80211, these hooks will be run before the device is suspended and after it resumes. Therefore, they can touch the hardware as much as they want to. Signed-off-by: Johannes Berg Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- include/net/cfg80211.h | 6 ++++++ net/wireless/sysfs.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c7da88fb15b..f19c3e16366 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -472,6 +472,9 @@ struct ieee80211_channel; * wireless extensions but this is subject to reevaluation as soon as this * code is used more widely and we have a first user without wext. * + * @suspend: wiphy device needs to be suspended + * @resume: wiphy device needs to be resumed + * * @add_virtual_intf: create a new virtual interface with the given name, * must set the struct wireless_dev's iftype. * @@ -525,6 +528,9 @@ struct ieee80211_channel; * @set_mgmt_extra_ie: Set extra IE data for management frames */ struct cfg80211_ops { + int (*suspend)(struct wiphy *wiphy); + int (*resume)(struct wiphy *wiphy); + int (*add_virtual_intf)(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 79a38287764..26a72b0797a 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -55,6 +55,34 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) } #endif +static int wiphy_suspend(struct device *dev, pm_message_t state) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int ret = 0; + + if (rdev->ops->suspend) { + rtnl_lock(); + ret = rdev->ops->suspend(&rdev->wiphy); + rtnl_unlock(); + } + + return ret; +} + +static int wiphy_resume(struct device *dev) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int ret = 0; + + if (rdev->ops->resume) { + rtnl_lock(); + ret = rdev->ops->resume(&rdev->wiphy); + rtnl_unlock(); + } + + return ret; +} + struct class ieee80211_class = { .name = "ieee80211", .owner = THIS_MODULE, @@ -63,6 +91,8 @@ struct class ieee80211_class = { #ifdef CONFIG_HOTPLUG .dev_uevent = wiphy_uevent, #endif + .suspend = wiphy_suspend, + .resume = wiphy_resume, }; int wiphy_sysfs_init(void) -- cgit v1.2.3-70-g09d2 From 5f936f11613c32ca7f8ed5fa333bb38a4501deeb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 21 Jan 2009 12:47:05 +0100 Subject: mac80211: constify ieee80211_if_conf.bssid Then one place can be a static const. Signed-off-by: Johannes Berg Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/libertas_tf/cmd.c | 2 +- drivers/net/wireless/libertas_tf/libertas_tf.h | 2 +- drivers/net/wireless/rt2x00/rt2x00config.c | 2 +- drivers/net/wireless/rt2x00/rt2x00lib.h | 2 +- include/net/mac80211.h | 2 +- net/mac80211/main.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c index 3d3914c83b1..28790e03dc4 100644 --- a/drivers/net/wireless/libertas_tf/cmd.c +++ b/drivers/net/wireless/libertas_tf/cmd.c @@ -286,7 +286,7 @@ void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); } -void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid) +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) { struct cmd_ds_set_bssid cmd; diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/libertas_tf/libertas_tf.h index 8995cd7c29b..4cc42dd5a00 100644 --- a/drivers/net/wireless/libertas_tf/libertas_tf.h +++ b/drivers/net/wireless/libertas_tf/libertas_tf.h @@ -463,7 +463,7 @@ int lbtf_set_radio_control(struct lbtf_private *priv); int lbtf_update_hw_spec(struct lbtf_private *priv); int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); -void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid); +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid); int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); int lbtf_set_channel(struct lbtf_private *priv, u8 channel); diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 6b3bb0f661d..9c2f5517af2 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -32,7 +32,7 @@ void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, enum nl80211_iftype type, - u8 *mac, u8 *bssid) + const u8 *mac, const u8 *bssid) { struct rt2x00intf_conf conf; unsigned int flags = 0; diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index daf0def915d..34efe465354 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -76,7 +76,7 @@ void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev); void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, enum nl80211_iftype type, - u8 *mac, u8 *bssid); + const u8 *mac, const u8 *bssid); void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct ieee80211_bss_conf *conf); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9a5869e9ecf..ef42559694f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -643,7 +643,7 @@ enum ieee80211_if_conf_change { */ struct ieee80211_if_conf { u32 changed; - u8 *bssid; + const u8 *bssid; }; /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c78304db475..6f0fe3564ca 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -176,7 +176,7 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) else if (sdata->vif.type == NL80211_IFTYPE_AP) conf.bssid = sdata->dev->dev_addr; else if (ieee80211_vif_is_mesh(&sdata->vif)) { - u8 zero[ETH_ALEN] = { 0 }; + static const u8 zero[ETH_ALEN] = { 0 }; conf.bssid = zero; } else { WARN_ON(1); -- cgit v1.2.3-70-g09d2 From 881d948c23442173a011f1adcfe4c95bf7f27515 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 21 Jan 2009 15:13:48 +0100 Subject: wireless: restrict to 32 legacy rates Since the standards only define 12 legacy rates, 32 is certainly a sane upper limit and we don't need to use u64 everywhere. Add sanity checking that no more than 32 rates are registered and change the variables to u32 throughout. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/b43legacy/main.c | 2 +- drivers/net/wireless/p54/p54.h | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- include/net/mac80211.h | 4 ++-- include/net/wireless.h | 2 +- net/mac80211/ieee80211_i.h | 6 +++--- net/mac80211/mesh.c | 2 +- net/mac80211/mesh.h | 2 +- net/mac80211/mesh_plink.c | 6 +++--- net/mac80211/mlme.c | 16 ++++++++-------- net/mac80211/util.c | 4 ++-- net/wireless/core.c | 12 +++++++++--- net/wireless/util.c | 2 +- 14 files changed, 35 insertions(+), 29 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 8bb6659c0b3..675a73a9807 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3398,7 +3398,7 @@ out_unlock_mutex: return err; } -static void b43_update_basic_rates(struct b43_wldev *dev, u64 brates) +static void b43_update_basic_rates(struct b43_wldev *dev, u32 brates) { struct ieee80211_supported_band *sband = dev->wl->hw->wiphy->bands[b43_current_band(dev->wl)]; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index fb996c27a19..879edc78671 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2650,7 +2650,7 @@ out_unlock_mutex: return err; } -static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u64 brates) +static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u32 brates) { struct ieee80211_supported_band *sband = dev->wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 64492feca9b..94c3acd1fca 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -144,7 +144,7 @@ struct p54_common { unsigned int output_power; u32 tsf_low32; u32 tsf_high32; - u64 basic_rate_mask; + u32 basic_rate_mask; u16 wakeup_timer; u16 aid; struct ieee80211_tx_queue_stats tx_stats[8]; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index cc56637e73b..46918deceda 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -396,7 +396,7 @@ struct rt2x00lib_erp { int ack_timeout; int ack_consume_time; - u64 basic_rates; + u32 basic_rates; int slot_time; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ef42559694f..c76484009ba 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -207,7 +207,7 @@ struct ieee80211_bss_conf { u16 beacon_int; u16 assoc_capability; u64 timestamp; - u64 basic_rates; + u32 basic_rates; struct ieee80211_bss_ht_conf ht; }; @@ -761,7 +761,7 @@ enum set_key_cmd { * sizeof(void *), size is determined in hw information. */ struct ieee80211_sta { - u64 supp_rates[IEEE80211_NUM_BANDS]; + u32 supp_rates[IEEE80211_NUM_BANDS]; u8 addr[ETH_ALEN]; u16 aid; struct ieee80211_sta_ht_cap ht_cap; diff --git a/include/net/wireless.h b/include/net/wireless.h index 9e73aae40c5..6c5b0820423 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -367,7 +367,7 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq) */ struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, - u64 basic_rates, int bitrate); + u32 basic_rates, int bitrate); /** * regulatory_hint - driver hint to the wireless core a regulatory domain diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a8c72742a8b..70366efc792 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -909,11 +909,11 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid); void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta); struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid, u8 *addr, u64 supp_rates); + u8 *bssid, u8 *addr, u32 supp_rates); int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason); int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); -u64 ieee80211_sta_get_rates(struct ieee80211_local *local, +u32 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee802_11_elems *elems, enum ieee80211_band band); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, @@ -1026,7 +1026,7 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq); -u64 ieee80211_mandatory_rates(struct ieee80211_local *local, +u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band); void ieee80211_dynamic_ps_enable_work(struct work_struct *work); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 82f568e9436..2d573f8470d 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -476,7 +476,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee802_11_elems elems; struct ieee80211_channel *channel; - u64 supp_rates = 0; + u32 supp_rates = 0; size_t baselen; int freq; enum ieee80211_band band = rx_status->band; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index f1196f5c3ef..9e064ee98ee 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -236,7 +236,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ -void mesh_neighbour_update(u8 *hw_addr, u64 rates, +void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, bool add); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c140a1b71a5..a8bbdeca013 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -93,7 +93,7 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) * on it in the lifecycle management section! */ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr, u64 rates) + u8 *hw_addr, u32 rates) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -222,7 +222,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, return 0; } -void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct ieee80211_sub_if_data *sdata, +void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, bool peer_accepting_plinks) { struct ieee80211_local *local = sdata->local; @@ -447,7 +447,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m spin_lock_bh(&sta->lock); } else if (!sta) { /* ftype == PLINK_OPEN */ - u64 rates; + u32 rates; if (!mesh_plink_free_count(sdata)) { mpl_dbg("Mesh plink error: no more free plinks\n"); rcu_read_unlock(); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b9e4b93089c..9852da54f5e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -73,7 +73,7 @@ static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie) static int ieee80211_compatible_rates(struct ieee80211_bss *bss, struct ieee80211_supported_band *sband, - u64 *rates) + u32 *rates) { int i, j, count; *rates = 0; @@ -93,14 +93,14 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, } /* also used by mesh code */ -u64 ieee80211_sta_get_rates(struct ieee80211_local *local, +u32 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee802_11_elems *elems, enum ieee80211_band band) { struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; size_t num_rates; - u64 supp_rates; + u32 supp_rates; int i, j; sband = local->hw.wiphy->bands[band]; @@ -253,7 +253,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; - u64 rates = 0; + u32 rates = 0; size_t e_ies_len; if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) { @@ -1282,7 +1282,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; struct sta_info *sta; - u64 rates, basic_rates; + u32 rates, basic_rates; u16 capab_info, status_code, aid; struct ieee802_11_elems elems; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; @@ -1639,7 +1639,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; struct ieee80211_channel *channel; u64 beacon_timestamp, rx_timestamp; - u64 supp_rates = 0; + u32 supp_rates = 0; enum ieee80211_band band = rx_status->band; if (elems->ds_params && elems->ds_params_len == 1) @@ -1660,7 +1660,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, sta = sta_info_get(local, mgmt->sa); if (sta) { - u64 prev_rates; + u32 prev_rates; prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ @@ -2526,7 +2526,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) * must be callable in atomic context. */ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid,u8 *addr, u64 supp_rates) + u8 *bssid,u8 *addr, u32 supp_rates) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3f559e3d0a7..ede96c4fea2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -731,12 +731,12 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz) return ret; } -u64 ieee80211_mandatory_rates(struct ieee80211_local *local, +u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band) { struct ieee80211_supported_band *sband; struct ieee80211_rate *bitrates; - u64 mandatory_rates; + u32 mandatory_rates; enum ieee80211_rate_flags mandatory_flag; int i; diff --git a/net/wireless/core.c b/net/wireless/core.c index b96fc0c3f1c..12522647608 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -273,10 +273,16 @@ int wiphy_register(struct wiphy *wiphy) sband->band = band; - if (!sband->n_channels || !sband->n_bitrates) { - WARN_ON(1); + if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) + return -EINVAL; + + /* + * Since we use a u32 for rate bitmaps in + * ieee80211_get_response_rate, we cannot + * have more than 32 legacy rates. + */ + if (WARN_ON(sband->n_bitrates > 32)) return -EINVAL; - } for (i = 0; i < sband->n_channels; i++) { sband->channels[i].orig_flags = diff --git a/net/wireless/util.c b/net/wireless/util.c index e76cc28b034..487cdd9bcff 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -9,7 +9,7 @@ struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, - u64 basic_rates, int bitrate) + u32 basic_rates, int bitrate) { struct ieee80211_rate *result = &sband->bitrates[0]; int i; -- cgit v1.2.3-70-g09d2 From 2134e7e724798cf8d54ae822afe235abb607d9b2 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 22 Jan 2009 09:00:52 +0530 Subject: mac80211: Add documentation bits for mac80211_rate_control_flags Signed-off-by: Sujith Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c76484009ba..634c08de6ac 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -262,6 +262,26 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), }; +/** + * enum mac80211_rate_control_flags - per-rate flags set by the + * Rate Control algorithm. + * + * These flags are set by the Rate control algorithm for each rate during tx, + * in the @flags member of struct ieee80211_tx_rate. + * + * @IEEE80211_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate. + * @IEEE80211_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required. + * This is set if the current BSS requires ERP protection. + * @IEEE80211_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. + * @IEEE80211_TX_RC_MCS: HT rate. + * @IEEE80211_TX_RC_GREEN_FIELD: Indicates whether this rate should be used in + * Greenfield mode. + * @IEEE80211_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be 40 MHz. + * @IEEE80211_TX_RC_DUP_DATA: The frame should be transmitted on both of the + * adjacent 20 MHz channels, if the current channel type is + * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. + * @IEEE80211_TX_RC_SHORT_GI: Short Guard interval should be used for this rate. + */ enum mac80211_rate_control_flags { IEEE80211_TX_RC_USE_RTS_CTS = BIT(0), IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1), -- cgit v1.2.3-70-g09d2 From 078e1e60dd6c6b0d4bc8d58ccb80c008e8efc9ff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Jan 2009 18:07:31 +0100 Subject: mac80211: Add capability to enable/disable beaconing This patch adds a flag to notify drivers to start and stop beaconing when needed, for example, during a scan run. Based on Sujith's first patch to do the same, but now disables beaconing for all virtual interfaces while scanning, has a separate change flag and tracks user-space requests. Signed-off-by: Sujith Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 9 +++++++-- net/mac80211/cfg.c | 5 +++-- net/mac80211/main.c | 42 +++++++++++++++++++++++++++++++++++++++++- net/mac80211/mesh.c | 3 ++- net/mac80211/mlme.c | 3 ++- net/mac80211/scan.c | 18 +++++++++++------- 6 files changed, 66 insertions(+), 14 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 634c08de6ac..35643c55827 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -646,10 +646,12 @@ struct ieee80211_if_init_conf { * @IEEE80211_IFCC_BSSID: The BSSID changed. * @IEEE80211_IFCC_BEACON: The beacon for this interface changed * (currently AP and MESH only), use ieee80211_beacon_get(). + * @IEEE80211_IFCC_BEACON_ENABLED: The enable_beacon value changed. */ enum ieee80211_if_conf_change { - IEEE80211_IFCC_BSSID = BIT(0), - IEEE80211_IFCC_BEACON = BIT(1), + IEEE80211_IFCC_BSSID = BIT(0), + IEEE80211_IFCC_BEACON = BIT(1), + IEEE80211_IFCC_BEACON_ENABLED = BIT(2), }; /** @@ -657,6 +659,8 @@ enum ieee80211_if_conf_change { * * @changed: parameters that have changed, see &enum ieee80211_if_conf_change. * @bssid: BSSID of the network we are associated to/creating. + * @enable_beacon: Indicates whether beacons can be sent. + * This is valid only for AP/IBSS/MESH modes. * * This structure is passed to the config_interface() callback of * &struct ieee80211_hw. @@ -664,6 +668,7 @@ enum ieee80211_if_conf_change { struct ieee80211_if_conf { u32 changed; const u8 *bssid; + bool enable_beacon; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3527de22caf..a1a1344c5c4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -523,7 +523,8 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, kfree(old); - return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); + return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | + IEEE80211_IFCC_BEACON_ENABLED); } static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, @@ -583,7 +584,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) synchronize_rcu(); kfree(old); - return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); + return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED); } /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6f0fe3564ca..8d5c19e4a1b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -168,7 +168,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) return 0; memset(&conf, 0, sizeof(conf)); - conf.changed = changed; if (sdata->vif.type == NL80211_IFTYPE_STATION || sdata->vif.type == NL80211_IFTYPE_ADHOC) @@ -183,9 +182,50 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) return -EINVAL; } + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + break; + default: + /* do not warn to simplify caller in scan.c */ + changed &= ~IEEE80211_IFCC_BEACON_ENABLED; + if (WARN_ON(changed & IEEE80211_IFCC_BEACON)) + return -EINVAL; + changed &= ~IEEE80211_IFCC_BEACON; + break; + } + + if (changed & IEEE80211_IFCC_BEACON_ENABLED) { + if (local->sw_scanning) { + conf.enable_beacon = false; + } else { + /* + * Beacon should be enabled, but AP mode must + * check whether there is a beacon configured. + */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + conf.enable_beacon = + !!rcu_dereference(sdata->u.ap.beacon); + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + conf.enable_beacon = true; + break; + default: + /* not reached */ + WARN_ON(1); + break; + } + } + } + if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID))) return -EINVAL; + conf.changed = changed; + return local->ops->config_interface(local_to_hw(local), &sdata->vif, &conf); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2d573f8470d..8a1fcaeee4f 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -442,7 +442,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ifmsh->housekeeping = true; queue_work(local->hw.workqueue, &ifmsh->work); - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); + ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | + IEEE80211_IFCC_BEACON_ENABLED); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9852da54f5e..ec400479c5f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1599,7 +1599,8 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ifsta->probe_resp = skb; - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); + ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | + IEEE80211_IFCC_BEACON_ENABLED); rates = 0; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index a2caeed57f4..8248d7b6ae8 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -472,8 +473,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { /* Tell AP we're back */ if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { @@ -482,8 +483,10 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) } } else netif_tx_wake_all_queues(sdata->dev); + + ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED); } - rcu_read_unlock(); + mutex_unlock(&local->iflist_mtx); done: ieee80211_mlme_notify_scan_completed(local); @@ -491,7 +494,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_scan_completed); - void ieee80211_scan_work(struct work_struct *work) { struct ieee80211_local *local = @@ -633,8 +635,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, local->sw_scanning = true; - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED); + if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { netif_tx_stop_all_queues(sdata->dev); @@ -643,7 +647,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, } else netif_tx_stop_all_queues(sdata->dev); } - rcu_read_unlock(); + mutex_unlock(&local->iflist_mtx); if (ssid) { local->scan_ssid_len = ssid_len; -- cgit v1.2.3-70-g09d2 From 1fa25e413659f943dfec65da2abe713d566c7fdf Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:44 -0800 Subject: cfg80211: add wiphy_apply_custom_regulatory() This adds wiphy_apply_custom_regulatory() to be used by drivers prior to wiphy registration to apply a custom regulatory domain. This can be used by drivers that do not have a direct 1-1 mapping between a regulatory domain and a country. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/wireless.h | 17 ++++++++ net/wireless/reg.c | 115 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 108 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/include/net/wireless.h b/include/net/wireless.h index 6c5b0820423..c5538b923af 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -401,4 +401,21 @@ extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2); extern void regulatory_hint_11d(struct wiphy *wiphy, u8 *country_ie, u8 country_ie_len); + +/** + * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain + * @wiphy: the wireless device we want to process the regulatory domain on + * @regd: the custom regulatory domain to use for this wiphy + * + * Drivers can sometimes have custom regulatory domains which do not apply + * to a specific country. Drivers can use this to apply such custom regulatory + * domains. This routine must be called prior to wiphy registration. The + * custom regulatory domain will be trusted completely and as such previous + * default channel settings will be disregarded. If no rule is found for a + * channel on the regulatory domain the channel will be disabled. + */ +extern void wiphy_apply_custom_regulatory( + struct wiphy *wiphy, + const struct ieee80211_regdomain *regd); + #endif /* __NET_WIRELESS_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b34fd84b3e2..0d6059502b4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -782,36 +782,18 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -/** - * freq_reg_info - get regulatory information for the given frequency - * @wiphy: the wiphy for which we want to process this rule for - * @center_freq: Frequency in KHz for which we want regulatory information for - * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one - * you can set this to 0. If this frequency is allowed we then set - * this value to the maximum allowed bandwidth. - * @reg_rule: the regulatory rule which we have for this frequency - * - * Use this function to get the regulatory rule for a specific frequency on - * a given wireless device. If the device has a specific regulatory domain - * it wants to follow we respect that unless a country IE has been received - * and processed already. - * - * Returns 0 if it was able to find a valid regulatory rule which does - * apply to the given center_freq otherwise it returns non-zero. It will - * also return -ERANGE if we determine the given center_freq does not even have - * a regulatory rule for a frequency range in the center_freq's band. See - * freq_in_rule_band() for our current definition of a band -- this is purely - * subjective and right now its 802.11 specific. - */ -static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, - const struct ieee80211_reg_rule **reg_rule) +static int freq_reg_info_regd(struct wiphy *wiphy, + u32 center_freq, + u32 *bandwidth, + 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; - regd = cfg80211_regdomain; + regd = custom_regd ? custom_regd : cfg80211_regdomain; /* Follow the driver's regulatory domain, if present, unless a country * IE has been processed */ @@ -852,6 +834,34 @@ static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, return !max_bandwidth; } +/** + * freq_reg_info - get regulatory information for the given frequency + * @wiphy: the wiphy for which we want to process this rule for + * @center_freq: Frequency in KHz for which we want regulatory information for + * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one + * you can set this to 0. If this frequency is allowed we then set + * this value to the maximum allowed bandwidth. + * @reg_rule: the regulatory rule which we have for this frequency + * + * Use this function to get the regulatory rule for a specific frequency on + * a given wireless device. If the device has a specific regulatory domain + * it wants to follow we respect that unless a country IE has been received + * and processed already. + * + * Returns 0 if it was able to find a valid regulatory rule which does + * apply to the given center_freq otherwise it returns non-zero. It will + * also return -ERANGE if we determine the given center_freq does not even have + * a regulatory rule for a frequency range in the center_freq's band. See + * freq_in_rule_band() for our current definition of a band -- this is purely + * subjective and right now its 802.11 specific. + */ +static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, + const struct ieee80211_reg_rule **reg_rule) +{ + return freq_reg_info_regd(wiphy, center_freq, + bandwidth, reg_rule, NULL); +} + static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx) { @@ -962,6 +972,63 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) wiphy->reg_notifier(wiphy, setby); } +static void handle_channel_custom(struct wiphy *wiphy, + enum ieee80211_band band, + unsigned int chan_idx, + const struct ieee80211_regdomain *regd) +{ + int r; + u32 max_bandwidth = 0; + const struct ieee80211_reg_rule *reg_rule = NULL; + const struct ieee80211_power_rule *power_rule = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + + sband = wiphy->bands[band]; + 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, ®_rule, regd); + + if (r) { + chan->flags = IEEE80211_CHAN_DISABLED; + return; + } + + power_rule = ®_rule->power_rule; + + chan->flags |= map_regdom_flags(reg_rule->flags); + chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); + chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); + chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); +} + +static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, + const struct ieee80211_regdomain *regd) +{ + 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++) + handle_channel_custom(wiphy, band, i, regd); +} + +/* Used by drivers prior to wiphy registration */ +void wiphy_apply_custom_regulatory(struct wiphy *wiphy, + const struct ieee80211_regdomain *regd) +{ + enum ieee80211_band band; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (wiphy->bands[band]) + handle_band_custom(wiphy, band, regd); + } +} +EXPORT_SYMBOL(wiphy_apply_custom_regulatory); + static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, const struct ieee80211_regdomain *src_regd) { -- cgit v1.2.3-70-g09d2 From 34f573473a659f8c2727d8d408e17b241900c28e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:45 -0800 Subject: cfg80211: export freq_reg_info() This can be used by drivers on the reg_notifier() Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/wireless.h | 24 ++++++++++++++++++++++++ net/wireless/reg.c | 24 ++---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) (limited to 'include/net') diff --git a/include/net/wireless.h b/include/net/wireless.h index c5538b923af..f68602eb416 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -418,4 +418,28 @@ extern void wiphy_apply_custom_regulatory( struct wiphy *wiphy, const struct ieee80211_regdomain *regd); +/** + * freq_reg_info - get regulatory information for the given frequency + * @wiphy: the wiphy for which we want to process this rule for + * @center_freq: Frequency in KHz for which we want regulatory information for + * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one + * you can set this to 0. If this frequency is allowed we then set + * this value to the maximum allowed bandwidth. + * @reg_rule: the regulatory rule which we have for this frequency + * + * Use this function to get the regulatory rule for a specific frequency on + * a given wireless device. If the device has a specific regulatory domain + * it wants to follow we respect that unless a country IE has been received + * and processed already. + * + * Returns 0 if it was able to find a valid regulatory rule which does + * apply to the given center_freq otherwise it returns non-zero. It will + * also return -ERANGE if we determine the given center_freq does not even have + * a regulatory rule for a frequency range in the center_freq's band. See + * freq_in_rule_band() for our current definition of a band -- this is purely + * subjective and right now its 802.11 specific. + */ +extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, + const struct ieee80211_reg_rule **reg_rule); + #endif /* __NET_WIRELESS_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0d6059502b4..d663795d694 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -833,29 +833,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy, return !max_bandwidth; } +EXPORT_SYMBOL(freq_reg_info); -/** - * freq_reg_info - get regulatory information for the given frequency - * @wiphy: the wiphy for which we want to process this rule for - * @center_freq: Frequency in KHz for which we want regulatory information for - * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one - * you can set this to 0. If this frequency is allowed we then set - * this value to the maximum allowed bandwidth. - * @reg_rule: the regulatory rule which we have for this frequency - * - * Use this function to get the regulatory rule for a specific frequency on - * a given wireless device. If the device has a specific regulatory domain - * it wants to follow we respect that unless a country IE has been received - * and processed already. - * - * Returns 0 if it was able to find a valid regulatory rule which does - * apply to the given center_freq otherwise it returns non-zero. It will - * also return -ERANGE if we determine the given center_freq does not even have - * a regulatory rule for a frequency range in the center_freq's band. See - * freq_in_rule_band() for our current definition of a band -- this is purely - * subjective and right now its 802.11 specific. - */ -static int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, +int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, const struct ieee80211_reg_rule **reg_rule) { return freq_reg_info_regd(wiphy, center_freq, -- cgit v1.2.3-70-g09d2 From 2a44f911d8bac3e6c97a25cc612e4324dfbdfdc4 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:49 -0800 Subject: cfg80211: rename fw_handles_regulatory to custom_regulatory Drivers without firmware can also have custom regulatory maps which do not map to a specific ISO / IEC alpha2 country code. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- include/net/wireless.h | 6 +++--- net/wireless/reg.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index f24d3b40e8e..9f284ebb6c2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -811,7 +811,7 @@ int iwl_setup_mac(struct iwl_priv *priv) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->fw_handles_regulatory = true; + hw->wiphy->custom_regulatory = true; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d520bfe2db9..de8c8d7ca0f 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -7011,7 +7011,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->fw_handles_regulatory = true; + hw->wiphy->custom_regulatory = true; /* 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/include/net/wireless.h b/include/net/wireless.h index f68602eb416..c3f6e462ec2 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -181,8 +181,8 @@ struct ieee80211_supported_band { * struct wiphy - wireless hardware description * @idx: the wiphy index assigned to this item * @class_dev: the class device representing /sys/class/ieee80211/ - * @fw_handles_regulatory: tells us the firmware for this device - * has its own regulatory solution and cannot identify the + * @custom_regulatory: tells us the driver for this device + * has its own custom regulatory domain and cannot identify the * ISO / IEC 3166 alpha2 it belongs to. When this is enabled * we will disregard the first regulatory hint (when the * initiator is %REGDOM_SET_BY_CORE). @@ -201,7 +201,7 @@ struct wiphy { /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ u16 interface_modes; - bool fw_handles_regulatory; + bool custom_regulatory; /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c201abd38ad..5db02a3d9c0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -927,7 +927,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) if (!last_request) return true; if (setby == REGDOM_SET_BY_CORE && - wiphy->fw_handles_regulatory) + wiphy->custom_regulatory) return true; return false; } -- cgit v1.2.3-70-g09d2 From 716f9392e2b84cacc18cc11f7427cb98adeb1c3d Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:51 -0800 Subject: cfg80211: pass more detailed regulatory request information on reg_notifier() Drivers may need more information than just who set the last regulatory domain, as such lets just pass the last regulatory_request receipt. To do this we need to move out to headers struct regulatory_request, and enum environment_cap. While at it lets add documentation for enum environment_cap. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/cfg80211.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/net/wireless.h | 3 ++- net/wireless/reg.c | 34 +--------------------------------- net/wireless/reg.h | 7 ------- 4 files changed, 48 insertions(+), 41 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f19c3e16366..dd1fd51638f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -357,6 +357,51 @@ enum reg_set_by { REGDOM_SET_BY_COUNTRY_IE, }; +/** + * enum environment_cap - Environment parsed from country IE + * @ENVIRON_ANY: indicates country IE applies to both indoor and + * outdoor operation. + * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation + * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation + */ +enum environment_cap { + ENVIRON_ANY, + ENVIRON_INDOOR, + ENVIRON_OUTDOOR, +}; + +/** + * struct regulatory_request - receipt of last regulatory request + * + * @wiphy: this is set if this request's initiator is + * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This + * can be used by the wireless core to deal with conflicts + * and potentially inform users of which devices specifically + * cased the conflicts. + * @initiator: indicates who sent this request, could be any of + * of those set in reg_set_by, %REGDOM_SET_BY_* + * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested + * regulatory domain. We have a few special codes: + * 00 - World regulatory domain + * 99 - built by driver but a specific alpha2 cannot be determined + * 98 - result of an intersection between two regulatory domains + * @intersect: indicates whether the wireless core should intersect + * the requested regulatory domain with the presently set regulatory + * domain. + * @country_ie_checksum: checksum of the last processed and accepted + * country IE + * @country_ie_env: lets us know if the AP is telling us we are outdoor, + * indoor, or if it doesn't matter + */ +struct regulatory_request { + struct wiphy *wiphy; + enum reg_set_by initiator; + char alpha2[2]; + bool intersect; + u32 country_ie_checksum; + enum environment_cap country_ie_env; +}; + struct ieee80211_freq_range { u32 start_freq_khz; u32 end_freq_khz; diff --git a/include/net/wireless.h b/include/net/wireless.h index c3f6e462ec2..9c19764a484 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -213,7 +213,8 @@ struct wiphy { struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; /* Lets us get back the wiphy on the callback */ - int (*reg_notifier)(struct wiphy *wiphy, enum reg_set_by setby); + int (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request); /* fields below are read-only, assigned by cfg80211 */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 81acb07f1d4..cad4daadba0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -42,38 +42,6 @@ #include "core.h" #include "reg.h" -/** - * struct regulatory_request - receipt of last regulatory request - * - * @wiphy: this is set if this request's initiator is - * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This - * can be used by the wireless core to deal with conflicts - * and potentially inform users of which devices specifically - * cased the conflicts. - * @initiator: indicates who sent this request, could be any of - * of those set in reg_set_by, %REGDOM_SET_BY_* - * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested - * regulatory domain. We have a few special codes: - * 00 - World regulatory domain - * 99 - built by driver but a specific alpha2 cannot be determined - * 98 - result of an intersection between two regulatory domains - * @intersect: indicates whether the wireless core should intersect - * the requested regulatory domain with the presently set regulatory - * domain. - * @country_ie_checksum: checksum of the last processed and accepted - * country IE - * @country_ie_env: lets us know if the AP is telling us we are outdoor, - * indoor, or if it doesn't matter - */ -struct regulatory_request { - struct wiphy *wiphy; - enum reg_set_by initiator; - char alpha2[2]; - bool intersect; - u32 country_ie_checksum; - enum environment_cap country_ie_env; -}; - /* Receipt of information from last regulatory request */ static struct regulatory_request *last_request; @@ -951,7 +919,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) handle_band(wiphy, band); } if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, setby); + wiphy->reg_notifier(wiphy, last_request); } static void handle_channel_custom(struct wiphy *wiphy, diff --git a/net/wireless/reg.h b/net/wireless/reg.h index a76ea3ff7cd..eb1dd5bc9b2 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -11,13 +11,6 @@ void regulatory_exit(void); int set_regdom(const struct ieee80211_regdomain *rd); -enum environment_cap { - ENVIRON_ANY, - ENVIRON_INDOOR, - ENVIRON_OUTDOOR, -}; - - /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this -- cgit v1.2.3-70-g09d2 From f976376de0d6a9697fb635369f12ae00251f4566 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:52 -0800 Subject: cfg80211: Allow for strict regulatory settings This allows drivers to request strict regulatory settings to be applied to its devices. This is desirable for devices where proper calibration and compliance can only be gauranteed for for the device's programmed regulatory domain. Regulatory domain settings will be ignored until the device's own regulatory domain is properly configured. If no regulatory domain is received only the world regulatory domain will be applied -- if OLD_REG (default to "US") is not enabled. If OLD_REG behaviour is not acceptable to drivers they must update their wiphy with a custom reuglatory prior to wiphy registration. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/wireless.h | 10 ++++++++++ net/wireless/reg.c | 28 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/wireless.h b/include/net/wireless.h index 9c19764a484..a42c1562d52 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -186,6 +186,15 @@ struct ieee80211_supported_band { * ISO / IEC 3166 alpha2 it belongs to. When this is enabled * we will disregard the first regulatory hint (when the * initiator is %REGDOM_SET_BY_CORE). + * @strict_regulatory: tells us the driver for this device will ignore + * regulatory domain settings until it gets its own regulatory domain + * via its regulatory_hint(). After its gets its own regulatory domain + * it will only allow further regulatory domain settings to further + * enhance compliance. For example if channel 13 and 14 are disabled + * by this regulatory domain no user regulatory domain can enable these + * channels at a later time. This can be used for devices which do not + * have calibration information gauranteed for frequencies or settings + * outside of its regulatory domain. * @reg_notifier: the driver's regulatory notification callback * @regd: the driver's regulatory domain, if one was requested via * the regulatory_hint() API. This can be used by the driver @@ -202,6 +211,7 @@ struct wiphy { u16 interface_modes; bool custom_regulatory; + bool strict_regulatory; /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cad4daadba0..89e0d8b3cf1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -867,6 +867,22 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, power_rule = ®_rule->power_rule; + if (last_request->initiator == REGDOM_SET_BY_DRIVER && + last_request->wiphy && last_request->wiphy == wiphy && + last_request->wiphy->strict_regulatory) { + /* This gaurantees the driver's requested regulatory domain + * will always be used as a base for further regulatory + * settings */ + chan->flags = chan->orig_flags = + map_regdom_flags(reg_rule->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_power = chan->orig_mpwr = + (int) MBM_TO_DBM(power_rule->max_eirp); + return; + } + chan->flags = flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, (int) MBI_TO_DBI(power_rule->max_antenna_gain)); @@ -897,6 +913,11 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) if (setby == REGDOM_SET_BY_CORE && wiphy->custom_regulatory) return true; + /* wiphy->regd will be set once the device has its own + * desired regulatory domain set */ + if (wiphy->strict_regulatory && !wiphy->regd && + !is_world_regdom(last_request->alpha2)) + return true; return false; } @@ -1155,10 +1176,15 @@ new_request: void regulatory_hint(struct wiphy *wiphy, const char *alpha2) { + int r; BUG_ON(!alpha2); mutex_lock(&cfg80211_drv_mutex); - __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); + r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, + alpha2, 0, ENVIRON_ANY); + /* This is required so that the orig_* parameters are saved */ + if (r == -EALREADY && wiphy->strict_regulatory) + wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER); mutex_unlock(&cfg80211_drv_mutex); } EXPORT_SYMBOL(regulatory_hint); -- cgit v1.2.3-70-g09d2 From 9a95371aa26e3cb9fb1340362912000088ff3c3e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 22 Jan 2009 15:05:53 -0800 Subject: mac80211: allow mac80211 drivers to get to struct ieee80211_hw from wiphy If a driver is given a wiphy and it wants to get to its private mac80211 driver area it can use wiphy_to_ieee80211_hw() to get first to its ieee80211_hw and then access the private structure via hw->priv. The wiphy_priv() is already being used internally by mac80211 and drivers should not use this. This can be helpful in a drivers reg_notifier(). Signed-off-by: Luis R. Rodriguez Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 13 +++++++++++++ net/mac80211/util.c | 9 +++++++++ 2 files changed, 22 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 35643c55827..c1e8261e899 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -989,6 +989,19 @@ struct ieee80211_hw { u8 max_rate_tries; }; +/** + * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy + * + * @wiphy: the &struct wiphy which we want to query + * + * mac80211 drivers can use this to get to their respective + * &struct ieee80211_hw. Drivers wishing to get to their own private + * structure can then access it via hw->priv. Note that mac802111 drivers should + * not use wiphy_priv() to try to get their private driver structure as this + * is already used internally by mac80211. + */ +struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy); + /** * SET_IEEE80211_DEV - set device for 802.11 hardware * diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ede96c4fea2..fc30f2940e1 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -41,6 +41,15 @@ const unsigned char rfc1042_header[] __aligned(2) = 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; + BUG_ON(!wiphy); + + local = wiphy_priv(wiphy); + return &local->hw; +} +EXPORT_SYMBOL(wiphy_to_ieee80211_hw); u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type) -- cgit v1.2.3-70-g09d2 From c771c9d8da1e8292ef8bf7fd4ce135dacc650130 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 23 Jan 2009 22:54:03 +0100 Subject: mac80211: add interface list lock Using only the RTNL has a number of problems, most notably that ieee80211_iterate_active_interfaces() and other interface list traversals cannot be done from the internal workqueue because it needs to be flushed under the RTNL. This patch introduces a new mutex that protects the interface list against modifications. A more detailed explanation is part of the code change. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ++--- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 31 +++++++++++++++++++++++++++++++ net/mac80211/main.c | 3 +++ net/mac80211/util.c | 4 ++-- 5 files changed, 40 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c1e8261e899..8e65adf0a64 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -928,9 +928,8 @@ enum ieee80211_hw_flags { * @workqueue: single threaded workqueue available for driver use, * allocated by mac80211 on registration and flushed when an * interface is removed. - * NOTICE: All work performed on this workqueue should NEVER - * acquire the RTNL lock (i.e. Don't use the function - * ieee80211_iterate_active_interfaces()) + * NOTICE: All work performed on this workqueue must not + * acquire the RTNL lock. * * @priv: pointer to private area that was allocated for driver use * along with this structure. diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 927cbde8c19..eaf3603862b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -643,7 +643,9 @@ struct ieee80211_local { struct crypto_blkcipher *wep_rx_tfm; u32 wep_iv; + /* see iface.c */ struct list_head interfaces; + struct mutex iflist_mtx; /* * Key lock, protects sdata's key_list and sta_info's diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8dc2c2188d9..00562a8b99c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -21,6 +21,23 @@ #include "mesh.h" #include "led.h" +/** + * DOC: Interface list locking + * + * The interface list in each struct ieee80211_local is protected + * three-fold: + * + * (1) modifications may only be done under the RTNL + * (2) modifications and readers are protected against each other by + * the iflist_mtx. + * (3) modifications are done in an RCU manner so atomic readers + * can traverse the list in RCU-safe blocks. + * + * As a consequence, reads (traversals) of the list can be protected + * by either the RTNL, the iflist_mtx or RCU. + */ + + static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) { int meshhdrlen; @@ -800,7 +817,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, params->mesh_id_len, params->mesh_id); + mutex_lock(&local->iflist_mtx); list_add_tail_rcu(&sdata->list, &local->interfaces); + mutex_unlock(&local->iflist_mtx); if (new_dev) *new_dev = ndev; @@ -816,7 +835,10 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) { ASSERT_RTNL(); + mutex_lock(&sdata->local->iflist_mtx); list_del_rcu(&sdata->list); + mutex_unlock(&sdata->local->iflist_mtx); + synchronize_rcu(); unregister_netdevice(sdata->dev); } @@ -832,7 +854,16 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) ASSERT_RTNL(); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { + /* + * we cannot hold the iflist_mtx across unregister_netdevice, + * but we only need to hold it for list modifications to lock + * out readers since we're under the RTNL here as all other + * writers. + */ + mutex_lock(&local->iflist_mtx); list_del(&sdata->list); + mutex_unlock(&local->iflist_mtx); + unregister_netdevice(sdata->dev); } } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 210dfe3cf6c..a109c06e8e4 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -758,6 +758,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.conf.radio_enabled = true; INIT_LIST_HEAD(&local->interfaces); + mutex_init(&local->iflist_mtx); spin_lock_init(&local->key_lock); @@ -1008,6 +1009,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); + mutex_destroy(&local->iflist_mtx); + wiphy_free(local->hw.wiphy); } EXPORT_SYMBOL(ieee80211_free_hw); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fc30f2940e1..73c7d7345ab 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -468,7 +468,7 @@ void ieee80211_iterate_active_interfaces( struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; - rtnl_lock(); + mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { switch (sdata->vif.type) { @@ -489,7 +489,7 @@ void ieee80211_iterate_active_interfaces( &sdata->vif); } - rtnl_unlock(); + mutex_unlock(&local->iflist_mtx); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); -- cgit v1.2.3-70-g09d2 From 3b5d665b51cda73ef1a774b515afd879a38e3674 Mon Sep 17 00:00:00 2001 From: Alina Friedrichsen Date: Sat, 24 Jan 2009 07:09:59 +0100 Subject: mac80211: Generic TSF debugging This patch enables low-level driver independent debugging of the TSF and remove the driver specific things of ath5k and ath9k from the debugfs. Signed-off-by: Alina Friedrichsen Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 10 +++++++ drivers/net/wireless/ath5k/debug.c | 48 --------------------------------- drivers/net/wireless/ath5k/debug.h | 1 - drivers/net/wireless/ath9k/core.h | 1 - drivers/net/wireless/ath9k/debug.c | 48 --------------------------------- drivers/net/wireless/ath9k/main.c | 9 +++++++ include/net/mac80211.h | 7 ++++- net/mac80211/debugfs.c | 55 +++++++++++++++++++++++++++++++++++--- 8 files changed, 77 insertions(+), 102 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index fa39f21c36c..b3f41acb906 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -239,6 +239,7 @@ static int ath5k_get_stats(struct ieee80211_hw *hw, static int ath5k_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); static u64 ath5k_get_tsf(struct ieee80211_hw *hw); +static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf); static void ath5k_reset_tsf(struct ieee80211_hw *hw); static int ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb); @@ -261,6 +262,7 @@ static struct ieee80211_ops ath5k_hw_ops = { .conf_tx = NULL, .get_tx_stats = ath5k_get_tx_stats, .get_tsf = ath5k_get_tsf, + .set_tsf = ath5k_set_tsf, .reset_tsf = ath5k_reset_tsf, .bss_info_changed = ath5k_bss_info_changed, }; @@ -3110,6 +3112,14 @@ ath5k_get_tsf(struct ieee80211_hw *hw) return ath5k_hw_get_tsf64(sc->ah); } +static void +ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath5k_softc *sc = hw->priv; + + ath5k_hw_set_tsf64(sc->ah, tsf); +} + static void ath5k_reset_tsf(struct ieee80211_hw *hw) { diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c index 129b72684da..413ed689cd5 100644 --- a/drivers/net/wireless/ath5k/debug.c +++ b/drivers/net/wireless/ath5k/debug.c @@ -193,50 +193,6 @@ static const struct file_operations fops_registers = { }; -/* debugfs: TSF */ - -static ssize_t read_file_tsf(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath5k_softc *sc = file->private_data; - char buf[100]; - snprintf(buf, sizeof(buf), "0x%016llx\n", - (unsigned long long)ath5k_hw_get_tsf64(sc->ah)); - return simple_read_from_buffer(user_buf, count, ppos, buf, 19); -} - -static ssize_t write_file_tsf(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct ath5k_softc *sc = file->private_data; - char buf[21]; - unsigned long long tsf; - - if (copy_from_user(buf, userbuf, min(count, sizeof(buf) - 1))) - return -EFAULT; - buf[sizeof(buf) - 1] = '\0'; - - if (strncmp(buf, "reset", 5) == 0) { - ath5k_hw_reset_tsf(sc->ah); - printk(KERN_INFO "debugfs reset TSF\n"); - } else { - tsf = simple_strtoul(buf, NULL, 0); - ath5k_hw_set_tsf64(sc->ah, tsf); - printk(KERN_INFO "debugfs set TSF to %#018llx\n", tsf); - } - - return count; -} - -static const struct file_operations fops_tsf = { - .read = read_file_tsf, - .write = write_file_tsf, - .open = ath5k_debugfs_open, - .owner = THIS_MODULE, -}; - - /* debugfs: beacons */ static ssize_t read_file_beacon(struct file *file, char __user *user_buf, @@ -430,9 +386,6 @@ ath5k_debug_init_device(struct ath5k_softc *sc) sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_registers); - sc->debug.debugfs_tsf = debugfs_create_file("tsf", S_IWUSR | S_IRUGO, - sc->debug.debugfs_phydir, sc, &fops_tsf); - sc->debug.debugfs_beacon = debugfs_create_file("beacon", S_IWUSR | S_IRUGO, sc->debug.debugfs_phydir, sc, &fops_beacon); @@ -451,7 +404,6 @@ ath5k_debug_finish_device(struct ath5k_softc *sc) { debugfs_remove(sc->debug.debugfs_debug); debugfs_remove(sc->debug.debugfs_registers); - debugfs_remove(sc->debug.debugfs_tsf); debugfs_remove(sc->debug.debugfs_beacon); debugfs_remove(sc->debug.debugfs_reset); debugfs_remove(sc->debug.debugfs_phydir); diff --git a/drivers/net/wireless/ath5k/debug.h b/drivers/net/wireless/ath5k/debug.h index ffc52939330..66f69f04e55 100644 --- a/drivers/net/wireless/ath5k/debug.h +++ b/drivers/net/wireless/ath5k/debug.h @@ -72,7 +72,6 @@ struct ath5k_dbg_info { struct dentry *debugfs_phydir; struct dentry *debugfs_debug; struct dentry *debugfs_registers; - struct dentry *debugfs_tsf; struct dentry *debugfs_beacon; struct dentry *debugfs_reset; }; diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index acbd8881ef8..29251f8dabb 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -141,7 +141,6 @@ struct ath9k_debug { struct dentry *debugfs_phy; struct dentry *debugfs_dma; struct dentry *debugfs_interrupt; - struct dentry *debugfs_tsf; struct ath_stats stats; }; diff --git a/drivers/net/wireless/ath9k/debug.c b/drivers/net/wireless/ath9k/debug.c index 05e1f82cc7a..1680164b4ad 100644 --- a/drivers/net/wireless/ath9k/debug.c +++ b/drivers/net/wireless/ath9k/debug.c @@ -223,48 +223,6 @@ static const struct file_operations fops_interrupt = { }; -static ssize_t read_file_tsf(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[100]; - snprintf(buf, sizeof(buf), "0x%016llx\n", - (unsigned long long)ath9k_hw_gettsf64(sc->sc_ah)); - return simple_read_from_buffer(user_buf, count, ppos, buf, 19); -} - -static ssize_t write_file_tsf(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[21]; - unsigned long long tsf; - - if (copy_from_user(buf, user_buf, min(count, sizeof(buf) - 1))) - return -EFAULT; - buf[sizeof(buf) - 1] = '\0'; - - if (strncmp(buf, "reset", 5) == 0) { - ath9k_hw_reset_tsf(sc->sc_ah); - printk(KERN_INFO "debugfs reset TSF\n"); - } else { - tsf = simple_strtoul(buf, NULL, 0); - ath9k_hw_settsf64(sc->sc_ah, tsf); - printk(KERN_INFO "debugfs set TSF to %#018llx\n", tsf); - } - - return count; -} - -static const struct file_operations fops_tsf = { - .read = read_file_tsf, - .write = write_file_tsf, - .open = ath9k_debugfs_open, - .owner = THIS_MODULE -}; - - int ath9k_init_debug(struct ath_softc *sc) { sc->sc_debug.debug_mask = ath9k_debug; @@ -290,11 +248,6 @@ int ath9k_init_debug(struct ath_softc *sc) if (!sc->sc_debug.debugfs_interrupt) goto err; - sc->sc_debug.debugfs_tsf = debugfs_create_file("tsf", S_IRUGO, - sc->sc_debug.debugfs_phy, sc, &fops_tsf); - if (!sc->sc_debug.debugfs_tsf) - goto err; - return 0; err: ath9k_exit_debug(sc); @@ -303,7 +256,6 @@ err: void ath9k_exit_debug(struct ath_softc *sc) { - debugfs_remove(sc->sc_debug.debugfs_tsf); debugfs_remove(sc->sc_debug.debugfs_interrupt); debugfs_remove(sc->sc_debug.debugfs_dma); debugfs_remove(sc->sc_debug.debugfs_phy); diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 15a7f90bc84..90e687b5a8b 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -2451,6 +2451,14 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw) return tsf; } +static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath_softc *sc = hw->priv; + struct ath_hal *ah = sc->sc_ah; + + ath9k_hw_settsf64(ah, tsf); +} + static void ath9k_reset_tsf(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; @@ -2514,6 +2522,7 @@ struct ieee80211_ops ath9k_ops = { .bss_info_changed = ath9k_bss_info_changed, .set_key = ath9k_set_key, .get_tsf = ath9k_get_tsf, + .set_tsf = ath9k_set_tsf, .reset_tsf = ath9k_reset_tsf, .ampdu_action = ath9k_ampdu_action, }; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8e65adf0a64..e2144f0e872 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1359,7 +1359,11 @@ enum ieee80211_ampdu_mlme_action { * hw->ampdu_queues items. * * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, - * this is only used for IBSS mode debugging and, as such, is not a + * this is only used for IBSS mode BSSID merging and debugging. Is not a + * required function. Must be atomic. + * + * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. + * Currently, this is only used for IBSS mode debugging. Is not a * required function. Must be atomic. * * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize @@ -1421,6 +1425,7 @@ struct ieee80211_ops { int (*get_tx_stats)(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); u64 (*get_tsf)(struct ieee80211_hw *hw); + void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf); void (*reset_tsf)(struct ieee80211_hw *hw); int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 717d5484e1e..e37f557de3f 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -57,12 +57,61 @@ DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", local->hw.conf.long_frame_max_tx_count); DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d", local->total_ps_buffered); -DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x", +DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x", local->wep_iv & 0xffffff); DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s", local->rate_ctrl ? local->rate_ctrl->ops->name : ""); -DEBUGFS_READONLY_FILE(tsf, 20, "%#018llx", - (unsigned long long) (local->ops->get_tsf ? local->ops->get_tsf(local_to_hw(local)) : 0)); + +static ssize_t tsf_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + u64 tsf = 0; + char buf[100]; + + if (local->ops->get_tsf) + tsf = local->ops->get_tsf(local_to_hw(local)); + + snprintf(buf, sizeof(buf), "0x%016llx\n", (unsigned long long) tsf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, 19); +} + +static ssize_t tsf_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + unsigned long long tsf; + char buf[100]; + size_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + if (strncmp(buf, "reset", 5) == 0) { + if (local->ops->reset_tsf) { + local->ops->reset_tsf(local_to_hw(local)); + printk(KERN_INFO "%s: debugfs reset TSF\n", wiphy_name(local->hw.wiphy)); + } + } else { + tsf = simple_strtoul(buf, NULL, 0); + if (local->ops->set_tsf) { + local->ops->set_tsf(local_to_hw(local), tsf); + printk(KERN_INFO "%s: debugfs set TSF to %#018llx\n", wiphy_name(local->hw.wiphy), tsf); + } + } + + return count; +} + +static const struct file_operations tsf_ops = { + .read = tsf_read, + .write = tsf_write, + .open = mac80211_open_file_generic +}; /* statistics stuff */ -- cgit v1.2.3-70-g09d2 From b00355db3f88d96810a60011a30cfb2c3469409d Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Sun, 1 Feb 2009 01:12:42 -0800 Subject: pkt_sched: sch_hfsc: sch_htb: Add non-work-conserving warning handler. Patrick McHardy suggested: > How about making this flag and the warning message (in a out-of-line > function) globally available? Other qdiscs (f.i. HFSC) can't deal with > inner non-work-conserving qdiscs as well. This patch uses qdisc->flags field of "suspected" child qdisc. Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 1 + include/net/sch_generic.h | 7 ++++--- net/sched/sch_api.c | 11 +++++++++++ net/sched/sch_hfsc.c | 6 ++---- net/sched/sch_htb.c | 9 +-------- 5 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 4082f39f507..e37fe3129c1 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -85,6 +85,7 @@ extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); extern void qdisc_put_stab(struct qdisc_size_table *tab); +extern void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc); extern void __qdisc_run(struct Qdisc *q); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f8c47429044..3d78a4d2246 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -42,9 +42,10 @@ struct Qdisc int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); struct sk_buff * (*dequeue)(struct Qdisc *dev); unsigned flags; -#define TCQ_F_BUILTIN 1 -#define TCQ_F_THROTTLED 2 -#define TCQ_F_INGRESS 4 +#define TCQ_F_BUILTIN 1 +#define TCQ_F_THROTTLED 2 +#define TCQ_F_INGRESS 4 +#define TCQ_F_WARN_NONWC (1 << 16) int padded; struct Qdisc_ops *ops; struct qdisc_size_table *stab; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 0fc4a18fd96..32009793307 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -444,6 +444,17 @@ out: } EXPORT_SYMBOL(qdisc_calculate_pkt_len); +void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc) +{ + if (!(qdisc->flags & TCQ_F_WARN_NONWC)) { + printk(KERN_WARNING + "%s: %s qdisc %X: is non-work-conserving?\n", + txt, qdisc->ops->id, qdisc->handle >> 16); + qdisc->flags |= TCQ_F_WARN_NONWC; + } +} +EXPORT_SYMBOL(qdisc_warn_nonwc); + static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 45c31b1a4e1..74226b26552 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -887,8 +887,7 @@ qdisc_peek_len(struct Qdisc *sch) skb = sch->ops->peek(sch); if (skb == NULL) { - if (net_ratelimit()) - printk("qdisc_peek_len: non work-conserving qdisc ?\n"); + qdisc_warn_nonwc("qdisc_peek_len", sch); return 0; } len = qdisc_pkt_len(skb); @@ -1642,8 +1641,7 @@ hfsc_dequeue(struct Qdisc *sch) skb = qdisc_dequeue_peeked(cl->qdisc); if (skb == NULL) { - if (net_ratelimit()) - printk("HFSC: Non-work-conserving qdisc ?\n"); + qdisc_warn_nonwc("HFSC", cl->qdisc); return NULL; } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 2f0f0b04d3f..77ff510ef8a 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -114,8 +114,6 @@ struct htb_class { struct tcf_proto *filter_list; int filter_cnt; - int warned; /* only one warning about non work conserving .. */ - /* token bucket parameters */ struct qdisc_rate_table *rate; /* rate table of the class itself */ struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */ @@ -809,13 +807,8 @@ next: skb = cl->un.leaf.q->dequeue(cl->un.leaf.q); if (likely(skb != NULL)) break; - if (!cl->warned) { - printk(KERN_WARNING - "htb: class %X isn't work conserving ?!\n", - cl->common.classid); - cl->warned = 1; - } + qdisc_warn_nonwc("htb", cl->un.leaf.q); htb_next_rb_node((level ? cl->parent->un.inner.ptr : q-> ptr[0]) + prio); cl = htb_lookup_leaf(q->row[level] + prio, prio, -- cgit v1.2.3-70-g09d2 From 24dd1fa184595ff095a92de807fdf029b2632673 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 1 Feb 2009 12:31:33 -0800 Subject: net: move bsockets outside of read only beginning of struct inet_hashinfo And switch bsockets to atomic_t since it might be changed in parallel. Signed-off-by: Eric Dumazet Acked-by: Evgeniy Polyakov Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 3 ++- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/inet_hashtables.c | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 8d98dc76bd7..a44e2248b2e 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -134,7 +134,7 @@ struct inet_hashinfo { struct inet_bind_hashbucket *bhash; unsigned int bhash_size; - int bsockets; + /* 4 bytes hole on 64 bit */ struct kmem_cache *bind_bucket_cachep; @@ -151,6 +151,7 @@ struct inet_hashinfo { struct inet_listen_hashbucket listening_hash[INET_LHTABLE_SIZE] ____cacheline_aligned_in_smp; + atomic_t bsockets; }; static inline struct inet_ehash_bucket *inet_ehash_bucket( diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 9bc6a187bdc..22cd19ee44e 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -119,7 +119,7 @@ again: (tb->num_owners < smallest_size || smallest_size == -1)) { smallest_size = tb->num_owners; smallest_rover = rover; - if (hashinfo->bsockets > (high - low) + 1) { + if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) { spin_unlock(&head->lock); snum = smallest_rover; goto have_snum; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index d7b6178bf48..625cc5f64c9 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -62,7 +62,7 @@ void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; - hashinfo->bsockets++; + atomic_inc(&hashinfo->bsockets); inet_sk(sk)->num = snum; sk_add_bind_node(sk, &tb->owners); @@ -81,7 +81,7 @@ static void __inet_put_port(struct sock *sk) struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash]; struct inet_bind_bucket *tb; - hashinfo->bsockets--; + atomic_dec(&hashinfo->bsockets); spin_lock(&head->lock); tb = inet_csk(sk)->icsk_bind_hash; @@ -532,6 +532,7 @@ void inet_hashinfo_init(struct inet_hashinfo *h) { int i; + atomic_set(&h->bsockets, 0); for (i = 0; i < INET_LHTABLE_SIZE; i++) { spin_lock_init(&h->listening_hash[i].lock); INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head, -- cgit v1.2.3-70-g09d2 From 4cc7f68d65558f683c702d4fe3a5aac4c5227b97 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 4 Feb 2009 16:55:54 -0800 Subject: net: Reexport sock_alloc_send_pskb The function sock_alloc_send_pskb is completely useless if not exported since most of the code in it won't be used as is. In fact, this code has already been duplicated in the tun driver. Now that we need accounting in the tun driver, we can in fact use this function as is. So this patch marks it for export again. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/net/sock.h | 5 +++++ net/core/sock.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/sock.h b/include/net/sock.h index 5a3a151bd73..c047def9cf1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -945,6 +945,11 @@ extern struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, int noblock, int *errcode); +extern struct sk_buff *sock_alloc_send_pskb(struct sock *sk, + unsigned long header_len, + unsigned long data_len, + int noblock, + int *errcode); extern void *sock_kmalloc(struct sock *sk, int size, gfp_t priority); extern void sock_kfree_s(struct sock *sk, void *mem, int size); diff --git a/net/core/sock.c b/net/core/sock.c index f3a0d08cbb4..c64996f8a27 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1254,10 +1254,9 @@ static long sock_wait_for_wmem(struct sock * sk, long timeo) * Generic send/receive buffer handlers */ -static struct sk_buff *sock_alloc_send_pskb(struct sock *sk, - unsigned long header_len, - unsigned long data_len, - int noblock, int *errcode) +struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, + unsigned long data_len, int noblock, + int *errcode) { struct sk_buff *skb; gfp_t gfp_mask; @@ -1337,6 +1336,7 @@ failure: *errcode = err; return NULL; } +EXPORT_SYMBOL(sock_alloc_send_pskb); struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, int noblock, int *errcode) -- cgit v1.2.3-70-g09d2 From 7fee5372d814c4be9546e5c28ac0058258d8df3e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 30 Jan 2009 11:13:06 +0100 Subject: mac80211: remove HW_SIGNAL_DB Giving the signal in dB isn't much more useful to userspace than giving the signal in unspecified units. This removes some radiotap information for zd1211 (the only driver using this flag), but it helps a lot for getting cfg80211-based scanning which won't support dB, and zd1211 being dB is a little fishy anyway. Signed-off-by: Johannes Berg Cc: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_mac.c | 2 +- include/net/mac80211.h | 22 ++++++++-------------- net/mac80211/main.c | 1 - net/mac80211/rx.c | 11 +---------- net/mac80211/wext.c | 3 +-- 5 files changed, 11 insertions(+), 28 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index a611ad85798..651807dfb50 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -967,7 +967,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band; hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_DB; + IEEE80211_HW_SIGNAL_UNSPEC; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_MESH_POINT) | diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e2144f0e872..409e2c69269 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -860,11 +860,6 @@ enum ieee80211_tkip_key_type { * expect values between 0 and @max_signal. * If possible please provide dB or dBm instead. * - * @IEEE80211_HW_SIGNAL_DB: - * Hardware gives signal values in dB, decibel difference from an - * arbitrary, fixed reference. We expect values between 0 and @max_signal. - * If possible please provide dBm instead. - * * @IEEE80211_HW_SIGNAL_DBM: * Hardware gives signal values in dBm, decibel difference from * one milliwatt. This is the preferred method since it is standardized @@ -900,15 +895,14 @@ enum ieee80211_hw_flags { IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE = 1<<3, IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = 1<<4, IEEE80211_HW_SIGNAL_UNSPEC = 1<<5, - IEEE80211_HW_SIGNAL_DB = 1<<6, - IEEE80211_HW_SIGNAL_DBM = 1<<7, - IEEE80211_HW_NOISE_DBM = 1<<8, - IEEE80211_HW_SPECTRUM_MGMT = 1<<9, - IEEE80211_HW_AMPDU_AGGREGATION = 1<<10, - IEEE80211_HW_SUPPORTS_PS = 1<<11, - IEEE80211_HW_PS_NULLFUNC_STACK = 1<<12, - IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<13, - IEEE80211_HW_MFP_CAPABLE = 1<<14, + IEEE80211_HW_SIGNAL_DBM = 1<<6, + IEEE80211_HW_NOISE_DBM = 1<<7, + IEEE80211_HW_SPECTRUM_MGMT = 1<<8, + IEEE80211_HW_AMPDU_AGGREGATION = 1<<9, + IEEE80211_HW_SUPPORTS_PS = 1<<10, + IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, + IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, + IEEE80211_HW_MFP_CAPABLE = 1<<13, }; /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a109c06e8e4..7247b303e96 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -884,7 +884,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->hw.conf.listen_interval = local->hw.max_listen_interval; local->wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | - IEEE80211_HW_SIGNAL_DB | IEEE80211_HW_SIGNAL_DBM) ? IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; local->wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ? diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1a59382976e..8e8ddbfcd23 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -86,8 +86,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, if (status->flag & RX_FLAG_TSFT) len += 8; - if (local->hw.flags & IEEE80211_HW_SIGNAL_DB || - local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; if (local->hw.flags & IEEE80211_HW_NOISE_DBM) len += 1; @@ -199,14 +198,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos = status->antenna; pos++; - /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */ - if (local->hw.flags & IEEE80211_HW_SIGNAL_DB) { - *pos = status->signal; - rthdr->it_present |= - cpu_to_le32(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL); - pos++; - } - /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */ /* IEEE80211_RADIOTAP_RX_FLAGS */ diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5c88b8246bb..bad1cfbfdf1 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -173,8 +173,7 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev, range->num_encoding_sizes = 2; range->max_encoding_tokens = NUM_DEFAULT_KEYS; - if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC || - local->hw.flags & IEEE80211_HW_SIGNAL_DB) + if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) range->max_qual.level = local->hw.max_signal; else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) range->max_qual.level = -110; -- cgit v1.2.3-70-g09d2 From 7b08b3b4a973de20d28d8a322c002c0a5444002a Mon Sep 17 00:00:00 2001 From: Alina Friedrichsen Date: Thu, 5 Feb 2009 17:58:34 +0100 Subject: mac80211: Remove TSF atomic requirement from the documentation The atomic requirement for the TSF callbacks is outdated. get_tsf() is only called by ieee80211_rx_bss_info() which is indirectly called by the work queue ieee80211_sta_work(). In the same context are called several other non-atomic functions, too. And the atomic requirement causes problems for drivers of USB wifi cards. Signed-off-by: Alina Friedrichsen Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 409e2c69269..341f3e595eb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1354,11 +1354,11 @@ enum ieee80211_ampdu_mlme_action { * * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, * this is only used for IBSS mode BSSID merging and debugging. Is not a - * required function. Must be atomic. + * required function. * * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. * Currently, this is only used for IBSS mode debugging. Is not a - * required function. Must be atomic. + * required function. * * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize * with other STAs in the IBSS. This is only used in IBSS mode. This -- cgit v1.2.3-70-g09d2 From 2a5193119269062608582418deba7af82844159a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Feb 2009 21:25:55 +0100 Subject: cfg80211/nl80211: scanning (and mac80211 update to use it) This patch adds basic scan capability to cfg80211/nl80211 and changes mac80211 to use it. The BSS list that cfg80211 maintains is made driver-accessible with a private area in each BSS struct, but mac80211 doesn't yet use it. That's another large project. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 12 +- drivers/net/wireless/iwlwifi/iwl-core.c | 1 + drivers/net/wireless/iwlwifi/iwl-scan.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 17 +- include/linux/nl80211.h | 65 +++ include/net/cfg80211.h | 131 +++++ include/net/mac80211.h | 6 +- include/net/wireless.h | 3 + net/mac80211/cfg.c | 20 + net/mac80211/ieee80211_i.h | 18 +- net/mac80211/iface.c | 2 +- net/mac80211/main.c | 32 +- net/mac80211/mlme.c | 37 +- net/mac80211/scan.c | 356 +++--------- net/mac80211/wext.c | 59 +- net/wireless/Makefile | 2 +- net/wireless/core.c | 8 + net/wireless/core.h | 20 + net/wireless/nl80211.c | 323 +++++++++++ net/wireless/nl80211.h | 8 + net/wireless/scan.c | 807 ++++++++++++++++++++++++++++ 21 files changed, 1546 insertions(+), 383 deletions(-) create mode 100644 net/wireless/scan.c (limited to 'include/net') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c196abc6db7..539960da7e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2678,11 +2678,19 @@ static void iwl_bss_info_changed(struct ieee80211_hw *hw, } -static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len) +static int iwl_mac_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { unsigned long flags; struct iwl_priv *priv = hw->priv; int ret; + u8 *ssid = NULL; + size_t ssid_len = 0; + + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; + } IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -2718,7 +2726,7 @@ static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len) if (ssid_len) { priv->one_direct_scan = 1; - priv->direct_ssid_len = min_t(u8, ssid_len, IW_ESSID_MAX_SIZE); + priv->direct_ssid_len = ssid_len; memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); } else { priv->one_direct_scan = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index e18c3f326f7..260bf903cb7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1271,6 +1271,7 @@ int iwl_setup_mac(struct iwl_priv *priv) BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->custom_regulatory = true; + hw->wiphy->max_scan_ssids = 1; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 22bad3ce7d6..1ec2b20eb37 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -860,7 +860,7 @@ void iwl_bg_scan_completed(struct work_struct *work) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - ieee80211_scan_completed(priv->hw); + ieee80211_scan_completed(priv->hw, false); /* Since setting the TXPOWER may have been deferred while * performing the scan, fire one off */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 42cc2884971..0cd8cb96a5e 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4442,15 +4442,23 @@ static void iwl3945_bss_info_changed(struct ieee80211_hw *hw, } -static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { int rc = 0; unsigned long flags; struct iwl_priv *priv = hw->priv; + size_t len = 0; + u8 *ssid = NULL; DECLARE_SSID_BUF(ssid_buf); IWL_DEBUG_MAC80211(priv, "enter\n"); + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + len = req->ssids[0].ssid_len; + } + mutex_lock(&priv->mutex); spin_lock_irqsave(&priv->lock, flags); @@ -4478,9 +4486,8 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) print_ssid(ssid_buf, ssid, len), len); priv->one_direct_scan = 1; - priv->direct_ssid_len = (u8) - min((u8) len, (u8) IW_ESSID_MAX_SIZE); - memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); + priv->direct_ssid_len = len; + memcpy(priv->direct_ssid, ssid, len); } else priv->one_direct_scan = 0; @@ -5412,6 +5419,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e hw->wiphy->custom_regulatory = true; + hw->wiphy->max_scan_ssids = 1; + /* 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4bc27049f4e..8802d1bda38 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -143,6 +143,13 @@ * added to all specified management frames generated by * kernel/firmware/driver. * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -192,6 +199,11 @@ enum nl80211_commands { NL80211_CMD_GET_REG, + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -305,6 +317,18 @@ enum nl80211_commands { * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with * %NL80211_CMD_SET_MGMT_EXTRA_IE). * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the + * scan result list changes (BSS expired or added) so that applications + * can verify that they got a single, consistent snapshot (when all dump + * messages carried the same generation number) + * @NL80211_ATTR_BSS: scan result BSS + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -372,6 +396,13 @@ enum nl80211_attrs { NL80211_ATTR_MGMT_SUBTYPE, NL80211_ATTR_IE, + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_SCAN_GENERATION, + NL80211_ATTR_BSS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -841,4 +872,38 @@ enum nl80211_channel_type { NL80211_CHAN_HT40MINUS, NL80211_CHAN_HT40PLUS }; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin) + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dd1fd51638f..09a0b268e5c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include /* remove once we remove the wext stuff */ #include @@ -504,6 +508,83 @@ struct wiphy; /* from net/ieee80211.h */ struct ieee80211_channel; +/** + * struct cfg80211_ssid - SSID description + * @ssid: the SSID + * @ssid_len: length of the ssid + */ +struct cfg80211_ssid { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; +}; + +/** + * struct cfg80211_scan_request - scan request description + * + * @ssids: SSIDs to scan for (active scan only) + * @n_ssids: number of SSIDs + * @channels: channels to scan on. + * @n_channels: number of channels for each band + * @wiphy: the wiphy this was for + * @ifidx: the interface index + */ +struct cfg80211_scan_request { + struct cfg80211_ssid *ssids; + int n_ssids; + struct ieee80211_channel **channels; + u32 n_channels; + + /* internal */ + struct wiphy *wiphy; + int ifidx; +}; + +/** + * enum cfg80211_signal_type - signal type + * + * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available + * @CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) + * @CFG80211_SIGNAL_TYPE_UNSPEC: signal strength, increasing from 0 through 100 + */ +enum cfg80211_signal_type { + CFG80211_SIGNAL_TYPE_NONE, + CFG80211_SIGNAL_TYPE_MBM, + CFG80211_SIGNAL_TYPE_UNSPEC, +}; + +/** + * struct cfg80211_bss - BSS description + * + * This structure describes a BSS (which may also be a mesh network) + * for use in scan results and similar. + * + * @bssid: BSSID of the BSS + * @tsf: timestamp of last received update + * @beacon_interval: the beacon interval as from the frame + * @capability: the capability field in host byte order + * @information_elements: the information elements (Note that there + * is no guarantee that these are well-formed!) + * @len_information_elements: total length of the information elements + * @signal: signal strength value + * @signal_type: signal type + * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes + */ +struct cfg80211_bss { + struct ieee80211_channel *channel; + + u8 bssid[ETH_ALEN]; + u64 tsf; + u16 beacon_interval; + u16 capability; + u8 *information_elements; + size_t len_information_elements; + + s32 signal; + enum cfg80211_signal_type signal_type; + + u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -571,6 +652,11 @@ struct ieee80211_channel; * @set_channel: Set channel * * @set_mgmt_extra_ie: Set extra IE data for management frames + * + * @scan: Request to do a scan. If returning zero, the scan request is given + * the driver, and will be valid until passed to cfg80211_scan_done(). + * For scan results, call cfg80211_inform_bss(); you can call this outside + * the scan/scan_done bracket too. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -648,6 +734,9 @@ struct cfg80211_ops { int (*set_mgmt_extra_ie)(struct wiphy *wiphy, struct net_device *dev, struct mgmt_extra_ie_params *params); + + int (*scan)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); }; /* temporary wext handlers */ @@ -658,5 +747,47 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); + +/** + * cfg80211_scan_done - notify that scan finished + * + * @request: the corresponding scan request + * @aborted: set to true if the scan was aborted for any reason, + * userspace will be notified of that + */ +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); + +/** + * cfg80211_inform_bss - inform cfg80211 of a new BSS + * + * @wiphy: the wiphy reporting the BSS + * @bss: the found BSS + * @gfp: context flags + * + * This informs cfg80211 that BSS information was found and + * the BSS should be updated/added. + */ +struct cfg80211_bss* +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, enum cfg80211_signal_type sigtype, + gfp_t gfp); + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len); +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg); +void cfg80211_put_bss(struct cfg80211_bss *bss); #endif /* __NET_CFG80211_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 341f3e595eb..88fa3e03e3e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1406,7 +1406,8 @@ struct ieee80211_ops { void (*update_tkip_key)(struct ieee80211_hw *hw, struct ieee80211_key_conf *conf, const u8 *address, u32 iv32, u16 *phase1key); - int (*hw_scan)(struct ieee80211_hw *hw, u8 *ssid, size_t len); + int (*hw_scan)(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req); int (*get_stats)(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, @@ -1844,8 +1845,9 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw); * mac80211 that the scan finished. * * @hw: the hardware that finished the scan + * @aborted: set to true if scan was aborted */ -void ieee80211_scan_completed(struct ieee80211_hw *hw); +void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted); /** * ieee80211_iterate_active_interfaces - iterate active interfaces diff --git a/include/net/wireless.h b/include/net/wireless.h index a42c1562d52..1c6285eb166 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -213,6 +213,9 @@ struct wiphy { bool custom_regulatory; bool strict_regulatory; + int bss_priv_size; + u8 max_scan_ssids; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 42d692fd9be..c8d969be440 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1277,6 +1277,25 @@ static int ieee80211_resume(struct wiphy *wiphy) #define ieee80211_resume NULL #endif +static int ieee80211_scan(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_scan_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + return ieee80211_request_scan(sdata, req); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1309,4 +1328,5 @@ struct cfg80211_ops mac80211_config_ops = { .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .suspend = ieee80211_suspend, .resume = ieee80211_resume, + .scan = ieee80211_scan, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9122416fd6a..cbc0b7d647f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -294,8 +294,6 @@ struct ieee80211_if_sta { u8 ssid[IEEE80211_MAX_SSID_LEN]; enum ieee80211_sta_mlme_state state; size_t ssid_len; - u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; - size_t scan_ssid_len; u16 aid; u16 ap_capab, capab; u8 *extra_ie; /* to be added to the end of AssocReq */ @@ -658,17 +656,18 @@ struct ieee80211_local { /* Scanning and BSS list */ bool sw_scanning, hw_scanning; + struct cfg80211_ssid scan_ssid; + struct cfg80211_scan_request int_scan_req; + struct cfg80211_scan_request *scan_req; + struct ieee80211_channel *scan_channel; int scan_channel_idx; - enum ieee80211_band scan_band; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; unsigned long last_scan_completed; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; - struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel; enum nl80211_channel_type oper_channel_type; - u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; - size_t scan_ssid_len; + struct ieee80211_channel *oper_channel, *csa_channel; struct list_head bss_list; struct ieee80211_bss *bss_hash[STA_HASH_SIZE]; spinlock_t bss_lock; @@ -929,7 +928,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local, /* scan/BSS handling */ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); int ieee80211_scan_results(struct ieee80211_local *local, struct iw_request_info *info, char *buf, size_t len); @@ -944,14 +943,15 @@ int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - int freq, bool beacon); + struct ieee80211_channel *channel, + bool beacon); struct ieee80211_bss * ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq, u8 *ssid, u8 ssid_len); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1c17fb8e405..df94b936526 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -522,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev) * scan event to userspace -- the scan is incomplete. */ if (local->sw_scanning) - ieee80211_scan_completed(&local->hw); + ieee80211_scan_completed(&local->hw, true); } conf.vif = &sdata->vif; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 956afea4214..954edfbb6b6 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -733,6 +733,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, return NULL; wiphy->privid = mac80211_wiphy_privid; + wiphy->max_scan_ssids = 4; local = wiphy_priv(wiphy); local->hw.wiphy = wiphy; @@ -817,25 +818,33 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) enum ieee80211_band band; struct net_device *mdev; struct ieee80211_master_priv *mpriv; + int channels, i, j; /* * generic code guarantees at least one band, * set this very early because much code assumes * that hw.conf.channel is assigned */ + channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[band]; - if (sband) { + if (sband && !local->oper_channel) { /* init channel we're on */ local->hw.conf.channel = local->oper_channel = local->scan_channel = &sband->channels[0]; - break; } + if (sband) + channels += sband->n_channels; } + local->int_scan_req.n_channels = channels; + local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req.channels) + return -ENOMEM; + /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -845,7 +854,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) result = wiphy_register(local->hw.wiphy); if (result < 0) - return result; + goto fail_wiphy_register; /* * We use the number of queues for feature tests (QoS, HT) internally @@ -948,6 +957,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_init(local); + /* alloc internal scan request */ + i = 0; + local->int_scan_req.ssids = &local->scan_ssid; + local->int_scan_req.n_ssids = 1; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!hw->wiphy->bands[band]) + continue; + for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) { + local->int_scan_req.channels[i] = + &hw->wiphy->bands[band]->channels[j]; + i++; + } + } + return 0; fail_wep: @@ -966,6 +989,8 @@ fail_workqueue: free_netdev(local->mdev); fail_mdev_alloc: wiphy_unregister(local->hw.wiphy); +fail_wiphy_register: + kfree(local->int_scan_req.channels); return result; } EXPORT_SYMBOL(ieee80211_register_hw); @@ -1011,6 +1036,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ieee80211_wep_free(local); ieee80211_led_exit(local); free_netdev(local->mdev); + kfree(local->int_scan_req.channels); } EXPORT_SYMBOL(ieee80211_unregister_hw); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bfc47b33068..46b4817cdea 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1743,7 +1743,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - freq, beacon); + channel, beacon); if (!bss) return; @@ -2162,7 +2162,15 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " "IBSS networks with same SSID (merge)\n", sdata->dev->name); - ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len); + + /* XXX maybe racy? */ + if (sdata->local->scan_req) + return; + + memcpy(sdata->local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_request_scan(sdata, &sdata->local->int_scan_req); } @@ -2378,8 +2386,15 @@ dont_join: IEEE80211_SCAN_INTERVAL)) { printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " "join\n", sdata->dev->name); - return ieee80211_request_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + + /* XXX maybe racy? */ + if (local->scan_req) + return -EBUSY; + + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + return ieee80211_request_scan(sdata, &local->int_scan_req); } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) { int interval = IEEE80211_SCAN_INTERVAL; @@ -2478,11 +2493,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata, } else { if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { ifsta->assoc_scan_tries++; + /* XXX maybe racy? */ + if (local->scan_req) + return -1; + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) - ieee80211_start_scan(sdata, NULL, 0); + local->int_scan_req.ssids[0].ssid_len = 0; else - ieee80211_start_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_start_scan(sdata, &local->int_scan_req); ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE; set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); } else { @@ -2520,8 +2540,7 @@ static void ieee80211_sta_work(struct work_struct *work) ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE && ifsta->state != IEEE80211_STA_MLME_ASSOCIATE && test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) { - ieee80211_start_scan(sdata, ifsta->scan_ssid, - ifsta->scan_ssid_len); + ieee80211_start_scan(sdata, local->scan_req); return; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index eddca4e1e13..c6b275b10cf 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -13,6 +13,9 @@ */ /* TODO: + * figure out how to avoid that the "current BSS" expires + * clean up IBSS code (in MLME), see why it adds a BSS to the list + * use cfg80211's BSS handling (depends on IBSS TODO above) * order BSS list by RSSI(?) ("quality of AP") * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, * SSID) @@ -225,10 +228,26 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - int freq, bool beacon) + struct ieee80211_channel *channel, + bool beacon) { struct ieee80211_bss *bss; - int clen; + int clen, freq = channel->center_freq; + enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE; + s32 signal = 0; + + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { + sigtype = CFG80211_SIGNAL_TYPE_MBM; + signal = rx_status->signal * 100; + } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { + sigtype = CFG80211_SIGNAL_TYPE_UNSPEC; + signal = (rx_status->signal * 100) / local->hw.max_signal; + } + + cfg80211_put_bss( + cfg80211_inform_bss_frame(local->hw.wiphy, channel, + mgmt, len, signal, sigtype, + GFP_ATOMIC)); #ifdef CONFIG_MAC80211_MESH if (elems->mesh_config) @@ -401,7 +420,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bss = ieee80211_bss_info_update(sdata->local, rx_status, mgmt, skb->len, &elems, - freq, beacon); + channel, beacon); if (bss) ieee80211_rx_bss_put(sdata->local, bss); @@ -439,26 +458,22 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, ieee80211_tx_skb(sdata, skb, 0); } -void ieee80211_scan_completed(struct ieee80211_hw *hw) +void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; - union iwreq_data wrqu; if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) return; - local->last_scan_completed = jiffies; - memset(&wrqu, 0, sizeof(wrqu)); + if (WARN_ON(!local->scan_req)) + return; - /* - * local->scan_sdata could have been NULLed by the interface - * down code in case we were scanning on an interface that is - * being taken down. - */ - sdata = local->scan_sdata; - if (sdata) - wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL); + if (local->scan_req != &local->int_scan_req) + cfg80211_scan_done(local->scan_req, aborted); + local->scan_req = NULL; + + local->last_scan_completed = jiffies; if (local->hw_scanning) { local->hw_scanning = false; @@ -520,9 +535,8 @@ void ieee80211_scan_work(struct work_struct *work) struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata = local->scan_sdata; - struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - int skip; + int skip, i; unsigned long next_delay = 0; /* @@ -533,33 +547,13 @@ void ieee80211_scan_work(struct work_struct *work) switch (local->scan_state) { case SCAN_SET_CHANNEL: - /* - * Get current scan band. scan_band may be IEEE80211_NUM_BANDS - * after we successfully scanned the last channel of the last - * band (and the last band is supported by the hw) - */ - if (local->scan_band < IEEE80211_NUM_BANDS) - sband = local->hw.wiphy->bands[local->scan_band]; - else - sband = NULL; - - /* - * If we are at an unsupported band and have more bands - * left to scan, advance to the next supported one. - */ - while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) { - local->scan_band++; - sband = local->hw.wiphy->bands[local->scan_band]; - local->scan_channel_idx = 0; - } - /* if no more bands/channels left, complete scan */ - if (!sband || local->scan_channel_idx >= sband->n_channels) { - ieee80211_scan_completed(local_to_hw(local)); + if (local->scan_channel_idx >= local->scan_req->n_channels) { + ieee80211_scan_completed(local_to_hw(local), false); return; } skip = 0; - chan = &sband->channels[local->scan_channel_idx]; + chan = local->scan_req->channels[local->scan_channel_idx]; if (chan->flags & IEEE80211_CHAN_DISABLED || (sdata->vif.type == NL80211_IFTYPE_ADHOC && @@ -575,15 +569,6 @@ void ieee80211_scan_work(struct work_struct *work) /* advance state machine to next channel/band */ local->scan_channel_idx++; - if (local->scan_channel_idx >= sband->n_channels) { - /* - * scan_band may end up == IEEE80211_NUM_BANDS, but - * we'll catch that case above and complete the scan - * if that is the case. - */ - local->scan_band++; - local->scan_channel_idx = 0; - } if (skip) break; @@ -596,10 +581,14 @@ void ieee80211_scan_work(struct work_struct *work) next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; local->scan_state = SCAN_SET_CHANNEL; - if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) + if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN || + !local->scan_req->n_ssids) break; - ieee80211_send_probe_req(sdata, NULL, local->scan_ssid, - local->scan_ssid_len); + for (i = 0; i < local->scan_req->n_ssids; i++) + ieee80211_send_probe_req( + sdata, NULL, + local->scan_req->ssids[i].ssid, + local->scan_req->ssids[i].ssid_len); next_delay = IEEE80211_CHANNEL_TIME; break; } @@ -610,14 +599,19 @@ void ieee80211_scan_work(struct work_struct *work) int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = scan_sdata->local; struct ieee80211_sub_if_data *sdata; - if (ssid_len > IEEE80211_MAX_SSID_LEN) + if (!req) return -EINVAL; + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS * BSSID: MACAddress @@ -645,7 +639,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, int rc; local->hw_scanning = true; - rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len); + rc = local->ops->hw_scan(local_to_hw(local), req); if (rc) { local->hw_scanning = false; return rc; @@ -678,15 +672,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, } mutex_unlock(&local->iflist_mtx); - if (ssid) { - local->scan_ssid_len = ssid_len; - memcpy(local->scan_ssid, ssid, ssid_len); - } else - local->scan_ssid_len = 0; local->scan_state = SCAN_SET_CHANNEL; local->scan_channel_idx = 0; - local->scan_band = IEEE80211_BAND_2GHZ; local->scan_sdata = scan_sdata; + local->scan_req = req; netif_addr_lock_bh(local->mdev); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; @@ -706,13 +695,21 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_sta *ifsta; + if (!req) + return -EINVAL; + + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + if (sdata->vif.type != NL80211_IFTYPE_STATION) - return ieee80211_start_scan(sdata, ssid, ssid_len); + return ieee80211_start_scan(sdata, req); /* * STA has a state machine that might need to defer scanning @@ -727,241 +724,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, } ifsta = &sdata->u.sta; - - ifsta->scan_ssid_len = ssid_len; - if (ssid_len) - memcpy(ifsta->scan_ssid, ssid, ssid_len); set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); queue_work(local->hw.workqueue, &ifsta->work); return 0; } - - -static void ieee80211_scan_add_ies(struct iw_request_info *info, - struct ieee80211_bss *bss, - char **current_ev, char *end_buf) -{ - u8 *pos, *end, *next; - struct iw_event iwe; - - if (bss == NULL || bss->ies == NULL) - return; - - /* - * If needed, fragment the IEs buffer (at IE boundaries) into short - * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. - */ - pos = bss->ies; - end = pos + bss->ies_len; - - while (end - pos > IW_GENERIC_IE_MAX) { - next = pos + 2 + pos[1]; - while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) - next = next + 2 + next[1]; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = next - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - - pos = next; - } - - if (end > pos) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = end - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - } -} - - -static char * -ieee80211_scan_result(struct ieee80211_local *local, - struct iw_request_info *info, - struct ieee80211_bss *bss, - char *current_ev, char *end_buf) -{ - struct iw_event iwe; - char *buf; - - if (time_after(jiffies, - bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) - return current_ev; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - if (bss_mesh_cfg(bss)) { - iwe.u.data.length = bss_mesh_id_len(bss); - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss_mesh_id(bss)); - } else { - iwe.u.data.length = bss->ssid_len; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->ssid); - } - - if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) - || bss_mesh_cfg(bss)) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (bss_mesh_cfg(bss)) - iwe.u.mode = IW_MODE_MESH; - else if (bss->capability & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = bss->freq; - iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - iwe.u.qual.qual = bss->qual; - iwe.u.qual.level = bss->signal; - iwe.u.qual.noise = bss->noise; - iwe.u.qual.updated = local->wstats_flags; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (bss->capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ""); - - ieee80211_scan_add_ies(info, bss, ¤t_ev, end_buf); - - if (bss->supp_rates_len > 0) { - /* display all supported rates in readable format */ - char *p = current_ev + iwe_stream_lcp_len(info); - int i; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 0; i < bss->supp_rates_len; i++) { - iwe.u.bitrate.value = ((bss->supp_rates[i] & - 0x7f) * 500000); - p = iwe_stream_add_value(info, current_ev, p, - end_buf, &iwe, IW_EV_PARAM_LEN); - } - current_ev = p; - } - - buf = kmalloc(30, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, " Last beacon: %dms ago", - jiffies_to_msecs(jiffies - bss->last_update)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - kfree(buf); - } - - if (bss_mesh_cfg(bss)) { - u8 *cfg = bss_mesh_cfg(bss); - buf = kmalloc(50, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh network (version %d)", cfg[0]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Protocol ID: " - "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], - cfg[4]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Metric ID: " - "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], - cfg[8]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Congestion Control Mode ID: " - "0x%02X%02X%02X%02X", cfg[9], cfg[10], - cfg[11], cfg[12]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Channel Precedence: " - "0x%02X%02X%02X%02X", cfg[13], cfg[14], - cfg[15], cfg[16]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - kfree(buf); - } - } - - return current_ev; -} - - -int ieee80211_scan_results(struct ieee80211_local *local, - struct iw_request_info *info, - char *buf, size_t len) -{ - char *current_ev = buf; - char *end_buf = buf + len; - struct ieee80211_bss *bss; - - spin_lock_bh(&local->bss_lock); - list_for_each_entry(bss, &local->bss_list, list) { - if (buf + len - current_ev <= IW_EV_ADDR_LEN) { - spin_unlock_bh(&local->bss_lock); - return -E2BIG; - } - current_ev = ieee80211_scan_result(local, info, bss, - current_ev, end_buf); - } - spin_unlock_bh(&local->bss_lock); - return current_ev - buf; -} diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index acd5808b87f..b337d7d5edb 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -173,8 +173,9 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev, range->num_encoding_sizes = 2; range->max_encoding_tokens = NUM_DEFAULT_KEYS; + /* cfg80211 requires this, and enforces 0..100 */ if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) - range->max_qual.level = local->hw.max_signal; + range->max_qual.level = 100; else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) range->max_qual.level = -110; else @@ -415,58 +416,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, } -static int ieee80211_ioctl_siwscan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct iw_scan_req *req = NULL; - u8 *ssid = NULL; - size_t ssid_len = 0; - - if (!netif_running(dev)) - return -ENETDOWN; - - if (sdata->vif.type != NL80211_IFTYPE_STATION && - sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - /* if SSID was specified explicitly then use that */ - if (wrqu->data.length == sizeof(struct iw_scan_req) && - wrqu->data.flags & IW_SCAN_THIS_ESSID) { - req = (struct iw_scan_req *)extra; - ssid = req->essid; - ssid_len = req->essid_len; - } - - return ieee80211_request_scan(sdata, ssid, ssid_len); -} - - -static int ieee80211_ioctl_giwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - int res; - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (local->sw_scanning || local->hw_scanning) - return -EAGAIN; - - res = ieee80211_scan_results(local, info, extra, data->length); - if (res >= 0) { - data->length = res; - return 0; - } - data->length = 0; - return res; -} - - static int ieee80211_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) @@ -1165,8 +1114,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ - (iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ + (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ (iw_handler) NULL, /* SIOCSIWNICKN */ diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 938a334c8db..dad43c24f69 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o 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 +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o cfg80211-$(CONFIG_NL80211) += nl80211.o diff --git a/net/wireless/core.c b/net/wireless/core.c index 12522647608..3cccd1390ce 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -240,6 +240,8 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) mutex_init(&drv->mtx); mutex_init(&drv->devlist_mtx); INIT_LIST_HEAD(&drv->netdev_list); + spin_lock_init(&drv->bss_lock); + INIT_LIST_HEAD(&drv->bss_list); device_initialize(&drv->wiphy.dev); drv->wiphy.dev.class = &ieee80211_class; @@ -259,6 +261,9 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; + if (WARN_ON(wiphy->max_scan_ssids < 1)) + return -EINVAL; + /* sanity check ifmodes */ WARN_ON(!ifmodes); ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; @@ -367,8 +372,11 @@ EXPORT_SYMBOL(wiphy_unregister); void cfg80211_dev_free(struct cfg80211_registered_device *drv) { + struct cfg80211_internal_bss *scan, *tmp; mutex_destroy(&drv->mtx); mutex_destroy(&drv->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) + kfree(scan); kfree(drv); } diff --git a/net/wireless/core.h b/net/wireless/core.h index f7fb9f41302..e29ad4cd464 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,6 +43,13 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; + /* BSSes/scanning */ + spinlock_t bss_lock; + struct list_head bss_list; + struct rb_root bss_tree; + u32 bss_generation; + struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + /* 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))); @@ -56,6 +65,15 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) extern struct mutex cfg80211_drv_mutex; extern struct list_head cfg80211_drv_list; +struct cfg80211_internal_bss { + struct list_head list; + struct rb_node rbn; + unsigned long ts; + struct kref ref; + /* must be last because of priv member */ + struct cfg80211_bss pub; +}; + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. @@ -94,4 +112,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, void ieee80211_set_bitrate_flags(struct wiphy *wiphy); void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); +void cfg80211_bss_expire(struct cfg80211_registered_device *dev); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d452396006e..298a4de5994 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include "core.h" @@ -109,6 +110,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, }; /* message building helper */ @@ -141,6 +144,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + dev->wiphy.max_scan_ssids); nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); if (!nl_modes) @@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, return err; } +static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_scan_request *request; + struct cfg80211_ssid *ssid; + struct ieee80211_channel *channel; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels = 0, i; + enum ieee80211_band band; + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + wiphy = &drv->wiphy; + + if (!drv->ops->scan) { + err = -EOPNOTSUPP; + goto out; + } + + rtnl_lock(); + + if (drv->scan_req) { + err = -EBUSY; + goto out_unlock; + } + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) + n_channels++; + if (!n_channels) { + err = -EINVAL; + goto out_unlock; + } + } else { + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) + n_ssids++; + + if (n_ssids > wiphy->max_scan_ssids) { + err = -EINVAL; + goto out_unlock; + } + + request = kzalloc(sizeof(*request) + + sizeof(*ssid) * n_ssids + + sizeof(channel) * n_channels, GFP_KERNEL); + if (!request) { + err = -ENOMEM; + goto out_unlock; + } + + request->channels = (void *)((char *)request + sizeof(*request)); + request->n_channels = n_channels; + if (n_ssids) + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = n_ssids; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + request->n_channels = n_channels; + i = 0; + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { + request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + if (!request->channels[i]) { + err = -EINVAL; + goto out_free; + } + i++; + } + } else { + /* all channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + request->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { + if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); + request->ssids[i].ssid_len = nla_len(attr); + i++; + } + } + + request->ifidx = dev->ifindex; + request->wiphy = &drv->wiphy; + + drv->scan_req = request; + err = drv->ops->scan(&drv->wiphy, dev, request); + + out_free: + if (err) { + drv->scan_req = NULL; + kfree(request); + } + out_unlock: + rtnl_unlock(); + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, + struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_bss *res) +{ + void *hdr; + struct nlattr *bss; + + hdr = nl80211hdr_put(msg, pid, seq, flags, + NL80211_CMD_NEW_SCAN_RESULTS); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, + rdev->bss_generation); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + + bss = nla_nest_start(msg, NL80211_ATTR_BSS); + if (!bss) + goto nla_put_failure; + if (!is_zero_ether_addr(res->bssid)) + NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); + if (res->information_elements && res->len_information_elements) + NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, + res->len_information_elements, + res->information_elements); + if (res->tsf) + NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); + if (res->beacon_interval) + NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); + NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); + NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); + + switch (res->signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); + break; + default: + break; + } + + nla_nest_end(msg, bss); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_scan(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + struct net_device *netdev; + struct cfg80211_internal_bss *scan; + int ifidx = cb->args[0]; + int start = cb->args[1], idx = 0; + int err; + + if (!ifidx) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + + if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); + if (!ifidx) + return -EINVAL; + cb->args[0] = ifidx; + } + + netdev = dev_get_by_index(&init_net, ifidx); + if (!netdev) + return -ENODEV; + + dev = cfg80211_get_dev_from_ifindex(ifidx); + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + goto out_put_netdev; + } + + spin_lock_bh(&dev->bss_lock); + cfg80211_bss_expire(dev); + + list_for_each_entry(scan, &dev->bss_list, list) { + if (++idx <= start) + continue; + if (nl80211_send_bss(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + dev, netdev, &scan->pub) < 0) { + idx--; + goto out; + } + } + + out: + spin_unlock_bh(&dev->bss_lock); + + cb->args[1] = idx; + err = skb->len; + cfg80211_put_dev(dev); + out_put_netdev: + dev_put(netdev); + + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_TRIGGER_SCAN, + .doit = nl80211_trigger_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_SCAN, + .policy = nl80211_policy, + .dumpit = nl80211_dump_scan, + }, }; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { .name = "config", }; +static struct genl_multicast_group nl80211_scan_mcgrp = { + .name = "scan", +}; /* notification functions */ @@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); } +static int nl80211_send_scan_donemsg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, + u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + /* XXX: we should probably bounce back the request? */ + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_NEW_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCAN_ABORTED) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -2488,6 +2807,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f3ea5c029ae..b565a5f84e9 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -7,6 +7,10 @@ extern int nl80211_init(void); extern void nl80211_exit(void); extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev); #else static inline int nl80211_init(void) { @@ -19,6 +23,10 @@ static inline void nl80211_notify_dev_rename( struct cfg80211_registered_device *rdev) { } +static inline void +nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{} #endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c new file mode 100644 index 00000000000..009d12810c5 --- /dev/null +++ b/net/wireless/scan.c @@ -0,0 +1,807 @@ +/* + * cfg80211 scan result handling + * + * Copyright 2008 Johannes Berg + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + struct net_device *dev; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + dev = dev_get_by_index(&init_net, request->ifidx); + if (!dev) + goto out; + + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + wiphy_to_dev(request->wiphy)->scan_req = NULL; + + if (aborted) + nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); + else + nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); + +#ifdef CONFIG_WIRELESS_EXT + if (!aborted) { + memset(&wrqu, 0, sizeof(wrqu)); + + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + } +#endif + + dev_put(dev); + + out: + kfree(request); +} +EXPORT_SYMBOL(cfg80211_scan_done); + +static void bss_release(struct kref *ref) +{ + struct cfg80211_internal_bss *bss; + + bss = container_of(ref, struct cfg80211_internal_bss, ref); + kfree(bss); +} + +/* must hold dev->bss_lock! */ +void cfg80211_bss_expire(struct cfg80211_registered_device *dev) +{ + struct cfg80211_internal_bss *bss, *tmp; + bool expired = false; + + list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + continue; + list_del(&bss->list); + rb_erase(&bss->rbn, &dev->bss_tree); + kref_put(&bss->ref, bss_release); + expired = true; + } + + if (expired) + dev->bss_generation++; +} + +static u8 *find_ie(u8 num, u8 *ies, size_t len) +{ + while (len > 2 && ies[0] != num) { + len -= ies[1] + 2; + ies += ies[1] + 2; + } + if (len < 2) + return NULL; + if (len < 2 + ies[1]) + return NULL; + return ies; +} + +static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) +{ + const u8 *ie1 = find_ie(num, ies1, len1); + const u8 *ie2 = find_ie(num, ies2, len2); + int r; + + if (!ie1 && !ie2) + return 0; + if (!ie1) + return -1; + + r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1])); + if (r == 0 && ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + return r; +} + +static bool is_bss(struct cfg80211_bss *a, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + const u8 *ssidie; + + if (compare_ether_addr(a->bssid, bssid)) + return false; + + ssidie = find_ie(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements); + if (!ssidie) + return false; + if (ssidie[1] != ssid_len) + return false; + return memcmp(ssidie + 2, ssid, ssid_len) == 0; +} + +static bool is_mesh(struct cfg80211_bss *a, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + const u8 *ie; + + if (!is_zero_ether_addr(a->bssid)) + return false; + + ie = find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + if (ie[1] != meshidlen) + return false; + if (memcmp(ie + 2, meshid, meshidlen)) + return false; + + ie = find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (ie[1] != IEEE80211_MESH_CONFIG_LEN) + return false; + + /* + * Ignore mesh capability (last two bytes of the IE) when + * comparing since that may differ between stations taking + * part in the same mesh. + */ + return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0; +} + +static int cmp_bss(struct cfg80211_bss *a, + struct cfg80211_bss *b) +{ + int r; + + if (a->channel != b->channel) + return b->channel->center_freq - a->channel->center_freq; + + r = memcmp(a->bssid, b->bssid, ETH_ALEN); + if (r) + return r; + + if (is_zero_ether_addr(a->bssid)) { + r = cmp_ies(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + if (r) + return r; + return cmp_ies(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + } + + return cmp_ies(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); +} + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (channel && bss->pub.channel != channel) + continue; + if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_bss); + +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (channel && bss->pub.channel != channel) + continue; + if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_mesh); + + +static void rb_insert_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) +{ + struct rb_node **p = &dev->bss_tree.rb_node; + struct rb_node *parent = NULL; + struct cfg80211_internal_bss *tbss; + int cmp; + + while (*p) { + parent = *p; + tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); + + cmp = cmp_bss(&bss->pub, &tbss->pub); + + if (WARN_ON(!cmp)) { + /* will sort of leak this BSS */ + return; + } + + if (cmp < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&bss->rbn, parent, p); + rb_insert_color(&bss->rbn, &dev->bss_tree); +} + +static struct cfg80211_internal_bss * +rb_find_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res) +{ + struct rb_node *n = dev->bss_tree.rb_node; + struct cfg80211_internal_bss *bss; + int r; + + while (n) { + bss = rb_entry(n, struct cfg80211_internal_bss, rbn); + r = cmp_bss(&res->pub, &bss->pub); + + if (r == 0) + return bss; + else if (r < 0) + n = n->rb_left; + else + n = n->rb_right; + } + + return NULL; +} + +static struct cfg80211_internal_bss * +cfg80211_bss_update(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res, + bool overwrite) +{ + struct cfg80211_internal_bss *found = NULL; + const u8 *meshid, *meshcfg; + + /* + * The reference to "res" is donated to this function. + */ + + if (WARN_ON(!res->pub.channel)) { + kref_put(&res->ref, bss_release); + return NULL; + } + + res->ts = jiffies; + + if (is_zero_ether_addr(res->pub.bssid)) { + /* must be mesh, verify */ + meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, + res->pub.len_information_elements); + meshcfg = find_ie(WLAN_EID_MESH_CONFIG, + res->pub.information_elements, + res->pub.len_information_elements); + if (!meshid || !meshcfg || + meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) { + /* bogus mesh */ + kref_put(&res->ref, bss_release); + return NULL; + } + } + + spin_lock_bh(&dev->bss_lock); + + found = rb_find_bss(dev, res); + + if (found && overwrite) { + list_replace(&found->list, &res->list); + rb_replace_node(&found->rbn, &res->rbn, + &dev->bss_tree); + kref_put(&found->ref, bss_release); + found = res; + } else if (found) { + kref_get(&found->ref); + found->pub.beacon_interval = res->pub.beacon_interval; + found->pub.tsf = res->pub.tsf; + found->pub.signal = res->pub.signal; + found->pub.signal_type = res->pub.signal_type; + found->pub.capability = res->pub.capability; + found->ts = res->ts; + kref_put(&res->ref, bss_release); + } else { + /* this "consumes" the reference */ + list_add_tail(&res->list, &dev->bss_list); + rb_insert_bss(dev, res); + found = res; + } + + dev->bss_generation++; + spin_unlock_bh(&dev->bss_lock); + + kref_get(&found->ref); + return found; +} + +struct cfg80211_bss * +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, enum cfg80211_signal_type sigtype, + gfp_t gfp) +{ + struct cfg80211_internal_bss *res; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + bool overwrite; + size_t privsz = wiphy->bss_priv_size; + + if (WARN_ON(sigtype == NL80211_BSS_SIGNAL_UNSPEC && + (signal < 0 || signal > 100))) + return NULL; + + if (WARN_ON(!mgmt || !wiphy || + len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) + return NULL; + + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); + if (!res) + return NULL; + + memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); + res->pub.channel = channel; + res->pub.signal_type = sigtype; + res->pub.signal = signal; + res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); + res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + /* point to after the private area */ + res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; + memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); + res->pub.len_information_elements = ielen; + + kref_init(&res->ref); + + overwrite = ieee80211_is_probe_resp(mgmt->frame_control); + + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); + if (!res) + return NULL; + + /* cfg80211_bss_update gives us a referenced result */ + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_inform_bss_frame); + +void cfg80211_put_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + kref_put(&bss->ref, bss_release); +} +EXPORT_SYMBOL(cfg80211_put_bss); + +#ifdef CONFIG_WIRELESS_EXT +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + struct iw_scan_req *wreq = NULL; + struct cfg80211_scan_request *creq; + int i, err, n_channels = 0; + enum ieee80211_band band; + + if (!netif_running(dev)) + return -ENETDOWN; + + rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + err = -EBUSY; + goto out; + } + + wiphy = &rdev->wiphy; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) { + err = -ENOMEM; + goto out; + } + + creq->wiphy = wiphy; + creq->ifidx = dev->ifindex; + creq->ssids = (void *)(creq + 1); + creq->channels = (void *)(creq->ssids + 1); + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* all channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + + /* translate scan request */ + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + wreq = (struct iw_scan_req *)extra; + + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); + creq->ssids[0].ssid_len = wreq->essid_len; + } + if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) + creq->n_ssids = 0; + } + + rdev->scan_req = creq; + err = rdev->ops->scan(wiphy, dev, creq); + if (err) { + rdev->scan_req = NULL; + kfree(creq); + } + out: + cfg80211_put_dev(rdev); + return err; +} +EXPORT_SYMBOL(cfg80211_wext_siwscan); + +static void ieee80211_scan_add_ies(struct iw_request_info *info, + struct cfg80211_bss *bss, + char **current_ev, char *end_buf) +{ + u8 *pos, *end, *next; + struct iw_event iwe; + + if (!bss->information_elements || + !bss->len_information_elements) + return; + + /* + * If needed, fragment the IEs buffer (at IE boundaries) into short + * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. + */ + pos = bss->information_elements; + end = pos + bss->len_information_elements; + + while (end - pos > IW_GENERIC_IE_MAX) { + next = pos + 2 + pos[1]; + while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) + next = next + 2 + next[1]; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = next - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + + pos = next; + } + + if (end > pos) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = end - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + } +} + + +static char * +ieee80211_bss(struct iw_request_info *info, + struct cfg80211_internal_bss *bss, + char *current_ev, char *end_buf) +{ + struct iw_event iwe; + u8 *buf, *cfg, *p; + u8 *ie = bss->pub.information_elements; + int rem = bss->pub.len_information_elements, i; + bool ismesh = false; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = bss->pub.channel->center_freq; + iwe.u.freq.e = 6; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + if (bss->pub.signal_type != CFG80211_SIGNAL_TYPE_NONE) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID; + switch (bss->pub.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + iwe.u.qual.level = bss->pub.signal / 100; + iwe.u.qual.updated |= IW_QUAL_DBM; + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + iwe.u.qual.level = bss->pub.signal; + break; + default: + /* not reached */ + break; + } + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); + + while (rem >= 2) { + /* invalid data */ + if (ie[1] > rem - 2) + break; + + switch (ie[0]) { + case WLAN_EID_SSID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_ID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_CONFIG: + ismesh = true; + if (ie[1] != IEEE80211_MESH_CONFIG_LEN) + break; + buf = kmalloc(50, GFP_ATOMIC); + if (!buf) + break; + cfg = ie + 2; + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "Mesh network (version %d)", cfg[0]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Path Selection Protocol ID: " + "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], + cfg[4]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Path Selection Metric ID: " + "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], + cfg[8]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Congestion Control Mode ID: " + "0x%02X%02X%02X%02X", cfg[9], cfg[10], + cfg[11], cfg[12]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Channel Precedence: " + "0x%02X%02X%02X%02X", cfg[13], cfg[14], + cfg[15], cfg[16]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + kfree(buf); + break; + case WLAN_EID_SUPP_RATES: + case WLAN_EID_EXT_SUPP_RATES: + /* display all supported rates in readable format */ + p = current_ev + iwe_stream_lcp_len(info); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 0; i < ie[1]; i++) { + iwe.u.bitrate.value = + ((ie[i + 2] & 0x7f) * 500000); + p = iwe_stream_add_value(info, current_ev, p, + end_buf, &iwe, IW_EV_PARAM_LEN); + } + current_ev = p; + break; + } + rem -= ie[1] + 2; + ie += ie[1] + 2; + } + + if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) + || ismesh) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (ismesh) + iwe.u.mode = IW_MODE_MESH; + else if (bss->pub.capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + buf = kmalloc(30, GFP_ATOMIC); + if (buf) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, " Last beacon: %dms ago", + jiffies_to_msecs(jiffies - bss->ts)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); + kfree(buf); + } + + ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); + + return current_ev; +} + + +static int ieee80211_scan_results(struct cfg80211_registered_device *dev, + struct iw_request_info *info, + char *buf, size_t len) +{ + char *current_ev = buf; + char *end_buf = buf + len; + struct cfg80211_internal_bss *bss; + + spin_lock_bh(&dev->bss_lock); + cfg80211_bss_expire(dev); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (buf + len - current_ev <= IW_EV_ADDR_LEN) { + spin_unlock_bh(&dev->bss_lock); + return -E2BIG; + } + current_ev = ieee80211_bss(info, bss, + current_ev, end_buf); + } + spin_unlock_bh(&dev->bss_lock); + return current_ev - buf; +} + + +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct cfg80211_registered_device *rdev; + int res; + + if (!netif_running(dev)) + return -ENETDOWN; + + rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + res = -EAGAIN; + goto out; + } + + res = ieee80211_scan_results(rdev, info, extra, data->length); + data->length = 0; + if (res >= 0) { + data->length = res; + res = 0; + } + + out: + cfg80211_put_dev(rdev); + return res; +} +EXPORT_SYMBOL(cfg80211_wext_giwscan); +#endif -- cgit v1.2.3-70-g09d2 From 78c1c7e109f1f14e7c18f290c4ebc58da220c7ba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Feb 2009 21:25:57 +0100 Subject: cfg80211: free_priv for BSS info When cfg80211 users have their own allocated data in the per-BSS private data, they will need to free this when the BSS struct is destroyed. Add a free_priv method and fix one place where the BSS was kfree'd rather than released properly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 ++ net/wireless/core.c | 2 +- net/wireless/scan.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 09a0b268e5c..b514abcc2b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -567,6 +567,7 @@ enum cfg80211_signal_type { * @len_information_elements: total length of the information elements * @signal: signal strength value * @signal_type: signal type + * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ struct cfg80211_bss { @@ -582,6 +583,7 @@ struct cfg80211_bss { s32 signal; enum cfg80211_signal_type signal_type; + void (*free_priv)(struct cfg80211_bss *bss); u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; diff --git a/net/wireless/core.c b/net/wireless/core.c index 3cccd1390ce..0668b2bfc1d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -376,7 +376,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *drv) mutex_destroy(&drv->mtx); mutex_destroy(&drv->devlist_mtx); list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) - kfree(scan); + cfg80211_put_bss(&scan->pub); kfree(drv); } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 009d12810c5..ec148f68a62 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -56,6 +56,8 @@ static void bss_release(struct kref *ref) struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); + if (bss->pub.free_priv) + bss->pub.free_priv(&bss->pub); kfree(bss); } -- cgit v1.2.3-70-g09d2 From d491af19db3adcc1eb1653e60a427fb4df36f361 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Feb 2009 21:25:58 +0100 Subject: cfg80211: allow users to request removing a BSS This patch introduces cfg80211_unlink_bss, a function to allow a driver to remove a BSS from the internal list and make it not show up in scan results any more -- this is to be used when the driver detects that the BSS is no longer available. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 11 +++++++++++ net/wireless/scan.c | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b514abcc2b9..f1d21570e6c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -791,5 +791,16 @@ struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, const u8 *meshid, size_t meshidlen, const u8 *meshcfg); void cfg80211_put_bss(struct cfg80211_bss *bss); +/** + * cfg80211_unlink_bss - unlink BSS from internal data structures + * @wiphy: the wiphy + * @bss: the bss to remove + * + * This function removes the given BSS from the internal data structures + * thereby making it no longer show up in scan results etc. Use this + * function when you detect a BSS is gone. Normally BSSes will also time + * out, so it is not necessary to use this function at all. + */ +void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); #endif /* __NET_CFG80211_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ec148f68a62..aacccc9ab6c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -432,6 +432,27 @@ void cfg80211_put_bss(struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_put_bss); +void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss; + + if (WARN_ON(!pub)) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + + spin_lock_bh(&dev->bss_lock); + + list_del(&bss->list); + rb_erase(&bss->rbn, &dev->bss_tree); + + spin_unlock_bh(&dev->bss_lock); + + kref_put(&bss->ref, bss_release); +} +EXPORT_SYMBOL(cfg80211_unlink_bss); + #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3-70-g09d2 From 79420f09e76e8e1dd1149d6ce9c20e06cbb5802a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Feb 2009 21:25:59 +0100 Subject: cfg80211: add more flexible BSS lookup Add a more flexible BSS lookup function so that mac80211 or other drivers can actually use this for getting the BSS to connect to. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 12 +++++++++++- net/wireless/scan.c | 10 ++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f1d21570e6c..c0d1f5b708c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -785,7 +785,17 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, - const u8 *ssid, size_t ssid_len); + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val); +static inline struct cfg80211_bss * +cfg80211_get_ibss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *ssid, size_t ssid_len) +{ + return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len, + WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); +} + struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index aacccc9ab6c..b1893c863b9 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -116,9 +116,12 @@ static bool is_bss(struct cfg80211_bss *a, { const u8 *ssidie; - if (compare_ether_addr(a->bssid, bssid)) + if (bssid && compare_ether_addr(a->bssid, bssid)) return false; + if (!ssid) + return true; + ssidie = find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); @@ -199,7 +202,8 @@ static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, - const u8 *ssid, size_t ssid_len) + const u8 *ssid, size_t ssid_len, + u16 capa_mask, u16 capa_val) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; @@ -207,6 +211,8 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { + if ((bss->pub.capability & capa_mask) != capa_val) + continue; if (channel && bss->pub.channel != channel) continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { -- cgit v1.2.3-70-g09d2 From f3a7c66b5ce0b75a9774a50b5dcce93e5ba28370 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sat, 14 Feb 2009 22:58:35 -0800 Subject: net: replace __constant_{endian} uses in net headers Base versions handle constant folding now. For headers exposed to userspace, we must only expose the __ prefixed versions. Signed-off-by: Harvey Harrison Signed-off-by: David S. Miller --- include/linux/if_pppox.h | 20 ++++----- include/linux/if_tunnel.h | 16 +++---- include/linux/ncp_no.h | 26 ++++++------ include/linux/netdevice.h | 4 +- include/linux/netfilter_bridge.h | 4 +- include/linux/pim.h | 4 +- include/linux/sctp.h | 90 ++++++++++++++++++++-------------------- include/linux/tcp.h | 20 ++++----- include/net/inet_ecn.h | 4 +- include/net/ip_vs.h | 4 +- include/net/ipv6.h | 4 +- include/net/ipx.h | 2 +- include/net/transp_v6.h | 2 +- include/rdma/ib_verbs.h | 2 +- 14 files changed, 101 insertions(+), 101 deletions(-) (limited to 'include/net') diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 30c88b2245f..90b5fae5d71 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -95,16 +95,16 @@ struct pppoe_tag { } __attribute ((packed)); /* Tag identifiers */ -#define PTT_EOL __constant_htons(0x0000) -#define PTT_SRV_NAME __constant_htons(0x0101) -#define PTT_AC_NAME __constant_htons(0x0102) -#define PTT_HOST_UNIQ __constant_htons(0x0103) -#define PTT_AC_COOKIE __constant_htons(0x0104) -#define PTT_VENDOR __constant_htons(0x0105) -#define PTT_RELAY_SID __constant_htons(0x0110) -#define PTT_SRV_ERR __constant_htons(0x0201) -#define PTT_SYS_ERR __constant_htons(0x0202) -#define PTT_GEN_ERR __constant_htons(0x0203) +#define PTT_EOL __cpu_to_be16(0x0000) +#define PTT_SRV_NAME __cpu_to_be16(0x0101) +#define PTT_AC_NAME __cpu_to_be16(0x0102) +#define PTT_HOST_UNIQ __cpu_to_be16(0x0103) +#define PTT_AC_COOKIE __cpu_to_be16(0x0104) +#define PTT_VENDOR __cpu_to_be16(0x0105) +#define PTT_RELAY_SID __cpu_to_be16(0x0110) +#define PTT_SRV_ERR __cpu_to_be16(0x0201) +#define PTT_SYS_ERR __cpu_to_be16(0x0202) +#define PTT_GEN_ERR __cpu_to_be16(0x0203) struct pppoe_hdr { #if defined(__LITTLE_ENDIAN_BITFIELD) diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index 82c43624c06..5a9aae4adb4 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -16,14 +16,14 @@ #define SIOCDELPRL (SIOCDEVPRIVATE + 6) #define SIOCCHGPRL (SIOCDEVPRIVATE + 7) -#define GRE_CSUM __constant_htons(0x8000) -#define GRE_ROUTING __constant_htons(0x4000) -#define GRE_KEY __constant_htons(0x2000) -#define GRE_SEQ __constant_htons(0x1000) -#define GRE_STRICT __constant_htons(0x0800) -#define GRE_REC __constant_htons(0x0700) -#define GRE_FLAGS __constant_htons(0x00F8) -#define GRE_VERSION __constant_htons(0x0007) +#define GRE_CSUM __cpu_to_be16(0x8000) +#define GRE_ROUTING __cpu_to_be16(0x4000) +#define GRE_KEY __cpu_to_be16(0x2000) +#define GRE_SEQ __cpu_to_be16(0x1000) +#define GRE_STRICT __cpu_to_be16(0x0800) +#define GRE_REC __cpu_to_be16(0x0700) +#define GRE_FLAGS __cpu_to_be16(0x00F8) +#define GRE_VERSION __cpu_to_be16(0x0007) struct ip_tunnel_parm { diff --git a/include/linux/ncp_no.h b/include/linux/ncp_no.h index f56a696a7cc..cddaa48fb18 100644 --- a/include/linux/ncp_no.h +++ b/include/linux/ncp_no.h @@ -2,18 +2,18 @@ #define _NCP_NO /* these define the attribute byte as seen by NCP */ -#define aRONLY (__constant_cpu_to_le32(1)) -#define aHIDDEN (__constant_cpu_to_le32(2)) -#define aSYSTEM (__constant_cpu_to_le32(4)) -#define aEXECUTE (__constant_cpu_to_le32(8)) -#define aDIR (__constant_cpu_to_le32(0x10)) -#define aARCH (__constant_cpu_to_le32(0x20)) -#define aSHARED (__constant_cpu_to_le32(0x80)) -#define aDONTSUBALLOCATE (__constant_cpu_to_le32(1L<<11)) -#define aTRANSACTIONAL (__constant_cpu_to_le32(1L<<12)) -#define aPURGE (__constant_cpu_to_le32(1L<<16)) -#define aRENAMEINHIBIT (__constant_cpu_to_le32(1L<<17)) -#define aDELETEINHIBIT (__constant_cpu_to_le32(1L<<18)) -#define aDONTCOMPRESS (__constant_cpu_to_le32(1L<<27)) +#define aRONLY (__cpu_to_le32(1)) +#define aHIDDEN (__cpu_to_le32(2)) +#define aSYSTEM (__cpu_to_le32(4)) +#define aEXECUTE (__cpu_to_le32(8)) +#define aDIR (__cpu_to_le32(0x10)) +#define aARCH (__cpu_to_le32(0x20)) +#define aSHARED (__cpu_to_le32(0x80)) +#define aDONTSUBALLOCATE (__cpu_to_le32(1L<<11)) +#define aTRANSACTIONAL (__cpu_to_le32(1L<<12)) +#define aPURGE (__cpu_to_le32(1L<<16)) +#define aRENAMEINHIBIT (__cpu_to_le32(1L<<17)) +#define aDELETEINHIBIT (__cpu_to_le32(1L<<18)) +#define aDONTCOMPRESS (__cpu_to_le32(1L<<27)) #endif /* _NCP_NO */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 355662aac94..bd8b4ca85a2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1863,7 +1863,7 @@ static inline int skb_bond_should_drop(struct sk_buff *skb) if (dev->priv_flags & IFF_SLAVE_INACTIVE) { if ((dev->priv_flags & IFF_SLAVE_NEEDARP) && - skb->protocol == __constant_htons(ETH_P_ARP)) + skb->protocol == __cpu_to_be16(ETH_P_ARP)) return 0; if (master->priv_flags & IFF_MASTER_ALB) { @@ -1872,7 +1872,7 @@ static inline int skb_bond_should_drop(struct sk_buff *skb) return 0; } if (master->priv_flags & IFF_MASTER_8023AD && - skb->protocol == __constant_htons(ETH_P_SLOW)) + skb->protocol == __cpu_to_be16(ETH_P_SLOW)) return 0; return 1; diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 499aa937590..f8105e54716 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -59,9 +59,9 @@ static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) { switch (skb->protocol) { - case __constant_htons(ETH_P_8021Q): + case __cpu_to_be16(ETH_P_8021Q): return VLAN_HLEN; - case __constant_htons(ETH_P_PPP_SES): + case __cpu_to_be16(ETH_P_PPP_SES): return PPPOE_SES_HLEN; default: return 0; diff --git a/include/linux/pim.h b/include/linux/pim.h index 1ba0661561a..252bf6644c5 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -4,14 +4,14 @@ #include /* Message types - V1 */ -#define PIM_V1_VERSION __constant_htonl(0x10000000) +#define PIM_V1_VERSION cpu_to_be32(0x10000000) #define PIM_V1_REGISTER 1 /* Message types - V2 */ #define PIM_VERSION 2 #define PIM_REGISTER 1 -#define PIM_NULL_REGISTER __constant_htonl(0x40000000) +#define PIM_NULL_REGISTER cpu_to_be32(0x40000000) /* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */ struct pimreghdr diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 8ba1c320f97..bd50b371ffa 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -172,35 +172,35 @@ typedef struct sctp_paramhdr { typedef enum { /* RFC 2960 Section 3.3.5 */ - SCTP_PARAM_HEARTBEAT_INFO = __constant_htons(1), + SCTP_PARAM_HEARTBEAT_INFO = cpu_to_be16(1), /* RFC 2960 Section 3.3.2.1 */ - SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5), - SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6), - SCTP_PARAM_STATE_COOKIE = __constant_htons(7), - SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8), - SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9), - SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11), - SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12), - SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000), + SCTP_PARAM_IPV4_ADDRESS = cpu_to_be16(5), + SCTP_PARAM_IPV6_ADDRESS = cpu_to_be16(6), + SCTP_PARAM_STATE_COOKIE = cpu_to_be16(7), + SCTP_PARAM_UNRECOGNIZED_PARAMETERS = cpu_to_be16(8), + SCTP_PARAM_COOKIE_PRESERVATIVE = cpu_to_be16(9), + SCTP_PARAM_HOST_NAME_ADDRESS = cpu_to_be16(11), + SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = cpu_to_be16(12), + SCTP_PARAM_ECN_CAPABLE = cpu_to_be16(0x8000), /* AUTH Extension Section 3 */ - SCTP_PARAM_RANDOM = __constant_htons(0x8002), - SCTP_PARAM_CHUNKS = __constant_htons(0x8003), - SCTP_PARAM_HMAC_ALGO = __constant_htons(0x8004), + SCTP_PARAM_RANDOM = cpu_to_be16(0x8002), + SCTP_PARAM_CHUNKS = cpu_to_be16(0x8003), + SCTP_PARAM_HMAC_ALGO = cpu_to_be16(0x8004), /* Add-IP: Supported Extensions, Section 4.2 */ - SCTP_PARAM_SUPPORTED_EXT = __constant_htons(0x8008), + SCTP_PARAM_SUPPORTED_EXT = cpu_to_be16(0x8008), /* PR-SCTP Sec 3.1 */ - SCTP_PARAM_FWD_TSN_SUPPORT = __constant_htons(0xc000), + SCTP_PARAM_FWD_TSN_SUPPORT = cpu_to_be16(0xc000), /* Add-IP Extension. Section 3.2 */ - SCTP_PARAM_ADD_IP = __constant_htons(0xc001), - SCTP_PARAM_DEL_IP = __constant_htons(0xc002), - SCTP_PARAM_ERR_CAUSE = __constant_htons(0xc003), - SCTP_PARAM_SET_PRIMARY = __constant_htons(0xc004), - SCTP_PARAM_SUCCESS_REPORT = __constant_htons(0xc005), - SCTP_PARAM_ADAPTATION_LAYER_IND = __constant_htons(0xc006), + SCTP_PARAM_ADD_IP = cpu_to_be16(0xc001), + SCTP_PARAM_DEL_IP = cpu_to_be16(0xc002), + SCTP_PARAM_ERR_CAUSE = cpu_to_be16(0xc003), + SCTP_PARAM_SET_PRIMARY = cpu_to_be16(0xc004), + SCTP_PARAM_SUCCESS_REPORT = cpu_to_be16(0xc005), + SCTP_PARAM_ADAPTATION_LAYER_IND = cpu_to_be16(0xc006), } sctp_param_t; /* enum */ @@ -212,13 +212,13 @@ typedef enum { * */ typedef enum { - SCTP_PARAM_ACTION_DISCARD = __constant_htons(0x0000), - SCTP_PARAM_ACTION_DISCARD_ERR = __constant_htons(0x4000), - SCTP_PARAM_ACTION_SKIP = __constant_htons(0x8000), - SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000), + SCTP_PARAM_ACTION_DISCARD = cpu_to_be16(0x0000), + SCTP_PARAM_ACTION_DISCARD_ERR = cpu_to_be16(0x4000), + SCTP_PARAM_ACTION_SKIP = cpu_to_be16(0x8000), + SCTP_PARAM_ACTION_SKIP_ERR = cpu_to_be16(0xc000), } sctp_param_action_t; -enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), }; +enum { SCTP_PARAM_ACTION_MASK = cpu_to_be16(0xc000), }; /* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */ @@ -457,17 +457,17 @@ typedef struct sctp_operr_chunk { */ typedef enum { - SCTP_ERROR_NO_ERROR = __constant_htons(0x00), - SCTP_ERROR_INV_STRM = __constant_htons(0x01), - SCTP_ERROR_MISS_PARAM = __constant_htons(0x02), - SCTP_ERROR_STALE_COOKIE = __constant_htons(0x03), - SCTP_ERROR_NO_RESOURCE = __constant_htons(0x04), - SCTP_ERROR_DNS_FAILED = __constant_htons(0x05), - SCTP_ERROR_UNKNOWN_CHUNK = __constant_htons(0x06), - SCTP_ERROR_INV_PARAM = __constant_htons(0x07), - SCTP_ERROR_UNKNOWN_PARAM = __constant_htons(0x08), - SCTP_ERROR_NO_DATA = __constant_htons(0x09), - SCTP_ERROR_COOKIE_IN_SHUTDOWN = __constant_htons(0x0a), + SCTP_ERROR_NO_ERROR = cpu_to_be16(0x00), + SCTP_ERROR_INV_STRM = cpu_to_be16(0x01), + SCTP_ERROR_MISS_PARAM = cpu_to_be16(0x02), + SCTP_ERROR_STALE_COOKIE = cpu_to_be16(0x03), + SCTP_ERROR_NO_RESOURCE = cpu_to_be16(0x04), + SCTP_ERROR_DNS_FAILED = cpu_to_be16(0x05), + SCTP_ERROR_UNKNOWN_CHUNK = cpu_to_be16(0x06), + SCTP_ERROR_INV_PARAM = cpu_to_be16(0x07), + SCTP_ERROR_UNKNOWN_PARAM = cpu_to_be16(0x08), + SCTP_ERROR_NO_DATA = cpu_to_be16(0x09), + SCTP_ERROR_COOKIE_IN_SHUTDOWN = cpu_to_be16(0x0a), /* SCTP Implementation Guide: @@ -476,9 +476,9 @@ typedef enum { * 13 Protocol Violation */ - SCTP_ERROR_RESTART = __constant_htons(0x0b), - SCTP_ERROR_USER_ABORT = __constant_htons(0x0c), - SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d), + SCTP_ERROR_RESTART = cpu_to_be16(0x0b), + SCTP_ERROR_USER_ABORT = cpu_to_be16(0x0c), + SCTP_ERROR_PROTO_VIOLATION = cpu_to_be16(0x0d), /* ADDIP Section 3.3 New Error Causes * @@ -493,11 +493,11 @@ typedef enum { * 0x0103 Association Aborted due to illegal ASCONF-ACK * 0x0104 Request refused - no authorization. */ - SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100), - SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101), - SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102), - SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103), - SCTP_ERROR_REQ_REFUSED = __constant_htons(0x0104), + SCTP_ERROR_DEL_LAST_IP = cpu_to_be16(0x0100), + SCTP_ERROR_RSRC_LOW = cpu_to_be16(0x0101), + SCTP_ERROR_DEL_SRC_IP = cpu_to_be16(0x0102), + SCTP_ERROR_ASCONF_ACK = cpu_to_be16(0x0103), + SCTP_ERROR_REQ_REFUSED = cpu_to_be16(0x0104), /* AUTH Section 4. New Error Cause * @@ -509,7 +509,7 @@ typedef enum { * -------------------------------------------------------------- * 0x0105 Unsupported HMAC Identifier */ - SCTP_ERROR_UNSUP_HMAC = __constant_htons(0x0105) + SCTP_ERROR_UNSUP_HMAC = cpu_to_be16(0x0105) } sctp_error_t; diff --git a/include/linux/tcp.h b/include/linux/tcp.h index fe77e1499ab..0cd99e6baca 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -69,16 +69,16 @@ union tcp_word_hdr { #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) enum { - TCP_FLAG_CWR = __constant_htonl(0x00800000), - TCP_FLAG_ECE = __constant_htonl(0x00400000), - TCP_FLAG_URG = __constant_htonl(0x00200000), - TCP_FLAG_ACK = __constant_htonl(0x00100000), - TCP_FLAG_PSH = __constant_htonl(0x00080000), - TCP_FLAG_RST = __constant_htonl(0x00040000), - TCP_FLAG_SYN = __constant_htonl(0x00020000), - TCP_FLAG_FIN = __constant_htonl(0x00010000), - TCP_RESERVED_BITS = __constant_htonl(0x0F000000), - TCP_DATA_OFFSET = __constant_htonl(0xF0000000) + TCP_FLAG_CWR = __cpu_to_be32(0x00800000), + TCP_FLAG_ECE = __cpu_to_be32(0x00400000), + TCP_FLAG_URG = __cpu_to_be32(0x00200000), + TCP_FLAG_ACK = __cpu_to_be32(0x00100000), + TCP_FLAG_PSH = __cpu_to_be32(0x00080000), + TCP_FLAG_RST = __cpu_to_be32(0x00040000), + TCP_FLAG_SYN = __cpu_to_be32(0x00020000), + TCP_FLAG_FIN = __cpu_to_be32(0x00010000), + TCP_RESERVED_BITS = __cpu_to_be32(0x0F000000), + TCP_DATA_OFFSET = __cpu_to_be32(0xF0000000) }; /* TCP socket options */ diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 7040a782c65..9b5d08f4f6e 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -113,12 +113,12 @@ static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner) static inline int INET_ECN_set_ce(struct sk_buff *skb) { switch (skb->protocol) { - case __constant_htons(ETH_P_IP): + case cpu_to_be16(ETH_P_IP): if (skb->network_header + sizeof(struct iphdr) <= skb->tail) return IP_ECN_set_ce(ip_hdr(skb)); break; - case __constant_htons(ETH_P_IPV6): + case cpu_to_be16(ETH_P_IPV6): if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail) return IP6_ECN_set_ce(ipv6_hdr(skb)); break; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index ab9b003ab67..bbae1e87efc 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -184,8 +184,8 @@ static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len, /* * The port number of FTP service (in network order). */ -#define FTPPORT __constant_htons(21) -#define FTPDATA __constant_htons(20) +#define FTPPORT cpu_to_be16(21) +#define FTPDATA cpu_to_be16(20) /* * TCP State Values diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 6d5b58a1c74..c1f16fc49ad 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -196,8 +196,8 @@ struct ip6_flowlabel struct net *fl_net; }; -#define IPV6_FLOWINFO_MASK __constant_htonl(0x0FFFFFFF) -#define IPV6_FLOWLABEL_MASK __constant_htonl(0x000FFFFF) +#define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) +#define IPV6_FLOWLABEL_MASK cpu_to_be32(0x000FFFFF) struct ipv6_fl_socklist { diff --git a/include/net/ipx.h b/include/net/ipx.h index 4cc0b4eca94..a14121dd193 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -27,7 +27,7 @@ struct ipx_address { struct ipxhdr { __be16 ipx_checksum __attribute__ ((packed)); -#define IPX_NO_CHECKSUM __constant_htons(0xFFFF) +#define IPX_NO_CHECKSUM cpu_to_be16(0xFFFF) __be16 ipx_pktsize __attribute__ ((packed)); __u8 ipx_tctrl; __u8 ipx_type; diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 876b6f2bb4f..bfb240c6cf7 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -46,7 +46,7 @@ extern int datagram_send_ctl(struct net *net, struct ipv6_txoptions *opt, int *hlimit, int *tclass); -#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006) +#define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006) /* * address family specific functions diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 936e333e7ce..c179318edd9 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -388,7 +388,7 @@ enum { IB_MULTICAST_QPN = 0xffffff }; -#define IB_LID_PERMISSIVE __constant_htons(0xFFFF) +#define IB_LID_PERMISSIVE cpu_to_be16(0xFFFF) enum ib_ah_flags { IB_AH_GRH = 1 -- cgit v1.2.3-70-g09d2 From 20d4947353be60e909e6b1a79d241457edd6833f Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:38 +0000 Subject: net: socket infrastructure for SO_TIMESTAMPING The overlap with the old SO_TIMESTAMP[NS] options is handled so that time stamping in software (net_enable_timestamp()) is enabled when SO_TIMESTAMP[NS] and/or SO_TIMESTAMPING_RX_SOFTWARE is set. It's disabled if all of these are off. Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- include/net/sock.h | 44 ++++++++++++++++++++++++++-- net/compat.c | 19 +++++++----- net/core/sock.c | 81 ++++++++++++++++++++++++++++++++++++++++++++-------- net/socket.c | 84 +++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 186 insertions(+), 42 deletions(-) (limited to 'include/net') diff --git a/include/net/sock.h b/include/net/sock.h index ded6854e3e4..cc9d5bcb06f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -158,7 +158,7 @@ struct sock_common { * @sk_allocation: allocation mode * @sk_sndbuf: size of send buffer in bytes * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, - * %SO_OOBINLINE settings + * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) @@ -488,6 +488,13 @@ enum sock_flags { SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ + SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */ + SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */ + SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */ + SOCK_TIMESTAMPING_RX_SOFTWARE, /* %SOF_TIMESTAMPING_RX_SOFTWARE */ + SOCK_TIMESTAMPING_SOFTWARE, /* %SOF_TIMESTAMPING_SOFTWARE */ + SOCK_TIMESTAMPING_RAW_HARDWARE, /* %SOF_TIMESTAMPING_RAW_HARDWARE */ + SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */ }; static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) @@ -1346,13 +1353,44 @@ static __inline__ void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { ktime_t kt = skb->tstamp; + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); - if (sock_flag(sk, SOCK_RCVTSTAMP)) + /* + * generate control messages if + * - receive time stamping in software requested (SOCK_RCVTSTAMP + * or SOCK_TIMESTAMPING_RX_SOFTWARE) + * - software time stamp available and wanted + * (SOCK_TIMESTAMPING_SOFTWARE) + * - hardware time stamps available and wanted + * (SOCK_TIMESTAMPING_SYS_HARDWARE or + * SOCK_TIMESTAMPING_RAW_HARDWARE) + */ + if (sock_flag(sk, SOCK_RCVTSTAMP) || + sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) || + (kt.tv64 && sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) || + (hwtstamps->hwtstamp.tv64 && + sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) || + (hwtstamps->syststamp.tv64 && + sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))) __sock_recv_timestamp(msg, sk, skb); else sk->sk_stamp = kt; } +/** + * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped + * @msg: outgoing packet + * @sk: socket sending this packet + * @shtx: filled with instructions for time stamping + * + * Currently only depends on SOCK_TIMESTAMPING* flags. Returns error code if + * parameters are invalid. + */ +extern int sock_tx_timestamp(struct msghdr *msg, + struct sock *sk, + union skb_shared_tx *shtx); + + /** * sk_eat_skb - Release a skb if it is no longer needed * @sk: socket to eat this skb from @@ -1421,7 +1459,7 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb) return NULL; } -extern void sock_enable_timestamp(struct sock *sk); +extern void sock_enable_timestamp(struct sock *sk, int flag); extern int sock_get_timestamp(struct sock *, struct timeval __user *); extern int sock_get_timestampns(struct sock *, struct timespec __user *); diff --git a/net/compat.c b/net/compat.c index a3a2ba0fac0..8d739053afe 100644 --- a/net/compat.c +++ b/net/compat.c @@ -216,7 +216,7 @@ Efault: int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) { struct compat_timeval ctv; - struct compat_timespec cts; + struct compat_timespec cts[3]; struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; struct compat_cmsghdr cmhdr; int cmlen; @@ -233,12 +233,17 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat data = &ctv; len = sizeof(ctv); } - if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS) { + if (level == SOL_SOCKET && + (type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) { + int count = type == SCM_TIMESTAMPNS ? 1 : 3; + int i; struct timespec *ts = (struct timespec *)data; - cts.tv_sec = ts->tv_sec; - cts.tv_nsec = ts->tv_nsec; + for (i = 0; i < count; i++) { + cts[i].tv_sec = ts[i].tv_sec; + cts[i].tv_nsec = ts[i].tv_nsec; + } data = &cts; - len = sizeof(cts); + len = sizeof(cts[0]) * count; } cmlen = CMSG_COMPAT_LEN(len); @@ -455,7 +460,7 @@ int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) struct timeval tv; if (!sock_flag(sk, SOCK_TIMESTAMP)) - sock_enable_timestamp(sk); + sock_enable_timestamp(sk, SOCK_TIMESTAMP); tv = ktime_to_timeval(sk->sk_stamp); if (tv.tv_sec == -1) return err; @@ -479,7 +484,7 @@ int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *usersta struct timespec ts; if (!sock_flag(sk, SOCK_TIMESTAMP)) - sock_enable_timestamp(sk); + sock_enable_timestamp(sk, SOCK_TIMESTAMP); ts = ktime_to_timespec(sk->sk_stamp); if (ts.tv_sec == -1) return err; diff --git a/net/core/sock.c b/net/core/sock.c index 4c64be4f876..40887e76652 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -120,6 +120,7 @@ #include #include #include +#include #include #include @@ -255,11 +256,14 @@ static void sock_warn_obsolete_bsdism(const char *name) } } -static void sock_disable_timestamp(struct sock *sk) +static void sock_disable_timestamp(struct sock *sk, int flag) { - if (sock_flag(sk, SOCK_TIMESTAMP)) { - sock_reset_flag(sk, SOCK_TIMESTAMP); - net_disable_timestamp(); + if (sock_flag(sk, flag)) { + sock_reset_flag(sk, flag); + if (!sock_flag(sk, SOCK_TIMESTAMP) && + !sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) { + net_disable_timestamp(); + } } } @@ -614,13 +618,38 @@ set_rcvbuf: else sock_set_flag(sk, SOCK_RCVTSTAMPNS); sock_set_flag(sk, SOCK_RCVTSTAMP); - sock_enable_timestamp(sk); + sock_enable_timestamp(sk, SOCK_TIMESTAMP); } else { sock_reset_flag(sk, SOCK_RCVTSTAMP); sock_reset_flag(sk, SOCK_RCVTSTAMPNS); } break; + case SO_TIMESTAMPING: + if (val & ~SOF_TIMESTAMPING_MASK) { + ret = EINVAL; + break; + } + sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE, + val & SOF_TIMESTAMPING_TX_HARDWARE); + sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE, + val & SOF_TIMESTAMPING_TX_SOFTWARE); + sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE, + val & SOF_TIMESTAMPING_RX_HARDWARE); + if (val & SOF_TIMESTAMPING_RX_SOFTWARE) + sock_enable_timestamp(sk, + SOCK_TIMESTAMPING_RX_SOFTWARE); + else + sock_disable_timestamp(sk, + SOCK_TIMESTAMPING_RX_SOFTWARE); + sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE, + val & SOF_TIMESTAMPING_SOFTWARE); + sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE, + val & SOF_TIMESTAMPING_SYS_HARDWARE); + sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE, + val & SOF_TIMESTAMPING_RAW_HARDWARE); + break; + case SO_RCVLOWAT: if (val < 0) val = INT_MAX; @@ -768,6 +797,24 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); break; + case SO_TIMESTAMPING: + v.val = 0; + if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE)) + v.val |= SOF_TIMESTAMPING_TX_HARDWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) + v.val |= SOF_TIMESTAMPING_TX_SOFTWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE)) + v.val |= SOF_TIMESTAMPING_RX_HARDWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) + v.val |= SOF_TIMESTAMPING_RX_SOFTWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) + v.val |= SOF_TIMESTAMPING_SOFTWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE)) + v.val |= SOF_TIMESTAMPING_SYS_HARDWARE; + if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) + v.val |= SOF_TIMESTAMPING_RAW_HARDWARE; + break; + case SO_RCVTIMEO: lv=sizeof(struct timeval); if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) { @@ -969,7 +1016,8 @@ void sk_free(struct sock *sk) rcu_assign_pointer(sk->sk_filter, NULL); } - sock_disable_timestamp(sk); + sock_disable_timestamp(sk, SOCK_TIMESTAMP); + sock_disable_timestamp(sk, SOCK_TIMESTAMPING_RX_SOFTWARE); if (atomic_read(&sk->sk_omem_alloc)) printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n", @@ -1787,7 +1835,7 @@ int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) { struct timeval tv; if (!sock_flag(sk, SOCK_TIMESTAMP)) - sock_enable_timestamp(sk); + sock_enable_timestamp(sk, SOCK_TIMESTAMP); tv = ktime_to_timeval(sk->sk_stamp); if (tv.tv_sec == -1) return -ENOENT; @@ -1803,7 +1851,7 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) { struct timespec ts; if (!sock_flag(sk, SOCK_TIMESTAMP)) - sock_enable_timestamp(sk); + sock_enable_timestamp(sk, SOCK_TIMESTAMP); ts = ktime_to_timespec(sk->sk_stamp); if (ts.tv_sec == -1) return -ENOENT; @@ -1815,11 +1863,20 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp) } EXPORT_SYMBOL(sock_get_timestampns); -void sock_enable_timestamp(struct sock *sk) +void sock_enable_timestamp(struct sock *sk, int flag) { - if (!sock_flag(sk, SOCK_TIMESTAMP)) { - sock_set_flag(sk, SOCK_TIMESTAMP); - net_enable_timestamp(); + if (!sock_flag(sk, flag)) { + sock_set_flag(sk, flag); + /* + * we just set one of the two flags which require net + * time stamping, but time stamping might have been on + * already because of the other one + */ + if (!sock_flag(sk, + flag == SOCK_TIMESTAMP ? + SOCK_TIMESTAMPING_RX_SOFTWARE : + SOCK_TIMESTAMP)) + net_enable_timestamp(); } } diff --git a/net/socket.c b/net/socket.c index 35dd7371752..47a3dc074eb 100644 --- a/net/socket.c +++ b/net/socket.c @@ -545,6 +545,18 @@ void sock_release(struct socket *sock) sock->file = NULL; } +int sock_tx_timestamp(struct msghdr *msg, struct sock *sk, + union skb_shared_tx *shtx) +{ + shtx->flags = 0; + if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE)) + shtx->hardware = 1; + if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) + shtx->software = 1; + return 0; +} +EXPORT_SYMBOL(sock_tx_timestamp); + static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size) { @@ -595,33 +607,65 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, return result; } +static int ktime2ts(ktime_t kt, struct timespec *ts) +{ + if (kt.tv64) { + *ts = ktime_to_timespec(kt); + return 1; + } else { + return 0; + } +} + /* * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP) */ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { - ktime_t kt = skb->tstamp; - - if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { - struct timeval tv; - /* Race occurred between timestamp enabling and packet - receiving. Fill in the current time for now. */ - if (kt.tv64 == 0) - kt = ktime_get_real(); - skb->tstamp = kt; - tv = ktime_to_timeval(kt); - put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv); - } else { - struct timespec ts; - /* Race occurred between timestamp enabling and packet - receiving. Fill in the current time for now. */ - if (kt.tv64 == 0) - kt = ktime_get_real(); - skb->tstamp = kt; - ts = ktime_to_timespec(kt); - put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts); + int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP); + struct timespec ts[3]; + int empty = 1; + struct skb_shared_hwtstamps *shhwtstamps = + skb_hwtstamps(skb); + + /* Race occurred between timestamp enabling and packet + receiving. Fill in the current time for now. */ + if (need_software_tstamp && skb->tstamp.tv64 == 0) + __net_timestamp(skb); + + if (need_software_tstamp) { + if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { + struct timeval tv; + skb_get_timestamp(skb, &tv); + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, + sizeof(tv), &tv); + } else { + struct timespec ts; + skb_get_timestampns(skb, &ts); + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, + sizeof(ts), &ts); + } + } + + + memset(ts, 0, sizeof(ts)); + if (skb->tstamp.tv64 && + sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) { + skb_get_timestampns(skb, ts + 0); + empty = 0; + } + if (shhwtstamps) { + if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE) && + ktime2ts(shhwtstamps->syststamp, ts + 1)) + empty = 0; + if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) && + ktime2ts(shhwtstamps->hwtstamp, ts + 2)) + empty = 0; } + if (!empty) + put_cmsg(msg, SOL_SOCKET, + SCM_TIMESTAMPING, sizeof(ts), &ts); } EXPORT_SYMBOL_GPL(__sock_recv_timestamp); -- cgit v1.2.3-70-g09d2 From 51f31cabe3ce5345b51e4a4f82138b38c4d5dc91 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:39 +0000 Subject: ip: support for TX timestamps on UDP and RAW sockets Instructions for time stamping outgoing packets are take from the socket layer and later copied into the new skb. Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 2 ++ include/net/ip.h | 1 + net/can/raw.c | 3 +++ net/ipv4/icmp.c | 2 ++ net/ipv4/ip_output.c | 6 ++++++ net/ipv4/raw.c | 1 + net/ipv4/udp.c | 4 ++++ 7 files changed, 19 insertions(+) (limited to 'include/net') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index a681a65b5bc..0e58b453917 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -56,6 +56,8 @@ and including the link layer, the scm_timestamping control message and a sock_extended_err control message with ee_errno==ENOMSG and ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending bounced packet is ready for reading as far as select() is concerned. +If the outgoing packet has to be fragmented, then only the first +fragment is time stamped and returned to the sending socket. All three values correspond to the same event in time, but were generated in different ways. Each of these values may be empty (= all diff --git a/include/net/ip.h b/include/net/ip.h index 10868139e65..4ac7577f98d 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -55,6 +55,7 @@ struct ipcm_cookie __be32 addr; int oif; struct ip_options *opt; + union skb_shared_tx shtx; }; #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) diff --git a/net/can/raw.c b/net/can/raw.c index 0703cba4bf9..6aa154e806a 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -646,6 +646,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, goto put_dev; err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err < 0) + goto free_skb; + err = sock_tx_timestamp(msg, sk, skb_tx(skb)); if (err < 0) goto free_skb; skb->dev = dev; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 705b33b184a..382800a62b3 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -375,6 +375,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) inet->tos = ip_hdr(skb)->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; + ipc.shtx.flags = 0; if (icmp_param->replyopts.optlen) { ipc.opt = &icmp_param->replyopts; if (ipc.opt->srr) @@ -532,6 +533,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) inet_sk(sk)->tos = tos; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts; + ipc.shtx.flags = 0; { struct flowi fl = { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 8ebe86dd72a..3e7e910c7c0 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -935,6 +935,10 @@ alloc_new_skb: sk->sk_allocation); if (unlikely(skb == NULL)) err = -ENOBUFS; + else + /* only the initial fragment is + time stamped */ + ipc->shtx.flags = 0; } if (skb == NULL) goto error; @@ -945,6 +949,7 @@ alloc_new_skb: skb->ip_summed = csummode; skb->csum = 0; skb_reserve(skb, hh_len); + *skb_tx(skb) = ipc->shtx; /* * Find where to start putting bytes. @@ -1364,6 +1369,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; + ipc.shtx.flags = 0; if (replyopts.opt.optlen) { ipc.opt = &replyopts.opt; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index dff8bc4e0fa..f774651f0a4 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -493,6 +493,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->saddr; ipc.opt = NULL; + ipc.shtx.flags = 0; ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c47c989cb1f..4bd178a111d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -596,6 +596,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, return -EOPNOTSUPP; ipc.opt = NULL; + ipc.shtx.flags = 0; if (up->pending) { /* @@ -643,6 +644,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->saddr; ipc.oif = sk->sk_bound_dev_if; + err = sock_tx_timestamp(msg, sk, &ipc.shtx); + if (err) + return err; if (msg->msg_controllen) { err = ip_cmsg_send(sock_net(sk), msg, &ipc); if (err) -- cgit v1.2.3-70-g09d2 From 06e868066e3b5828383eb40ff4d1c0029100b0b5 Mon Sep 17 00:00:00 2001 From: Lucas Nussbaum Date: Fri, 13 Feb 2009 08:33:41 +0000 Subject: sctp: Allow to disable SCTP checksums via module parameter This is a new version of my patch, now using a module parameter instead of a sysctl, so that the option is harder to find. Please note that, once the module is loaded, it is still possible to change the value of the parameter in /sys/module/sctp/parameters/, which is useful if you want to do performance comparisons without rebooting. Computation of SCTP checksums significantly affects the performance of SCTP. For example, using two dual-Opteron 246 connected using a Gbe network, it was not possible to achieve more than ~730 Mbps, compared to 941 Mbps after disabling SCTP checksums. Unfortunately, SCTP checksum offloading in NICs is not commonly available (yet). By default, checksums are still enabled, of course. Signed-off-by: Lucas Nussbaum Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 5 +++++ net/sctp/input.c | 3 ++- net/sctp/output.c | 2 +- net/sctp/protocol.c | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 9661d7b765f..9f70d54fcef 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -218,6 +218,10 @@ extern struct sctp_globals { /* Flag to idicate if SCTP-AUTH is enabled */ int auth_enable; + + /* Flag to indicate whether computing and verifying checksum + * is disabled. */ + int checksum_disable; } sctp_globals; #define sctp_rto_initial (sctp_globals.rto_initial) @@ -252,6 +256,7 @@ extern struct sctp_globals { #define sctp_addip_noauth (sctp_globals.addip_noauth_enable) #define sctp_prsctp_enable (sctp_globals.prsctp_enable) #define sctp_auth_enable (sctp_globals.auth_enable) +#define sctp_checksum_disable (sctp_globals.checksum_disable) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/input.c b/net/sctp/input.c index 2e4a8646dbc..693fd080481 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -142,7 +142,8 @@ int sctp_rcv(struct sk_buff *skb) __skb_pull(skb, skb_transport_offset(skb)); if (skb->len < sizeof(struct sctphdr)) goto discard_it; - if (!skb_csum_unnecessary(skb) && sctp_rcv_checksum(skb) < 0) + if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) && + sctp_rcv_checksum(skb) < 0) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); diff --git a/net/sctp/output.c b/net/sctp/output.c index 47bfba6c03e..2d65b7a7330 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -531,7 +531,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) * Note: Adler-32 is no longer applicable, as has been replaced * by CRC32-C as described in . */ - if (!(dst->dev->features & NETIF_F_NO_CSUM)) { + if (!sctp_checksum_disable && !(dst->dev->features & NETIF_F_NO_CSUM)) { crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); crc32 = sctp_end_cksum(crc32); } else diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index b78e3be6901..cc0b592698f 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1411,4 +1411,6 @@ MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-132"); MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-132"); MODULE_AUTHOR("Linux Kernel SCTP developers "); MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); +module_param_named(no_checksums, sctp_checksum_disable, bool, 0644); +MODULE_PARM_DESC(no_checksums, "Disable checksums computing and verification"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 4458f04c02a46c679a90ef71f866a415c192deb4 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 13 Feb 2009 08:33:42 +0000 Subject: sctp: Clean up sctp checksumming code The sctp crc32c checksum is always generated in little endian. So, we clean up the code to treat it as little endian and remove all the __force casts. Suggested by Herbert Xu. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/sctp.h | 2 +- include/net/sctp/checksum.h | 14 +++++++------- net/sctp/input.c | 11 ++++++----- net/sctp/output.c | 14 ++++++-------- 4 files changed, 20 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index bd50b371ffa..c2731bfe04d 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -60,7 +60,7 @@ typedef struct sctphdr { __be16 source; __be16 dest; __be32 vtag; - __be32 checksum; + __le32 checksum; } __attribute__((packed)) sctp_sctphdr_t; #ifdef __KERNEL__ diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 2fec3c366e8..befc8d2a1b9 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -46,14 +46,14 @@ #include #include -static inline __be32 sctp_crc32c(__be32 crc, u8 *buffer, u16 length) +static inline __u32 sctp_crc32c(__u32 crc, u8 *buffer, u16 length) { - return (__force __be32)crc32c((__force u32)crc, buffer, length); + return crc32c(crc, buffer, length); } -static inline __be32 sctp_start_cksum(__u8 *buffer, __u16 length) +static inline __u32 sctp_start_cksum(__u8 *buffer, __u16 length) { - __be32 crc = ~cpu_to_be32(0); + __u32 crc = ~(__u32)0; __u8 zero[sizeof(__u32)] = {0}; /* Optimize this routine to be SCTP specific, knowing how @@ -72,12 +72,12 @@ static inline __be32 sctp_start_cksum(__u8 *buffer, __u16 length) return crc; } -static inline __be32 sctp_update_cksum(__u8 *buffer, __u16 length, __be32 crc32) +static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32) { return sctp_crc32c(crc32, buffer, length); } -static inline __be32 sctp_end_cksum(__be32 crc32) +static inline __le32 sctp_end_cksum(__be32 crc32) { - return (__force __be32)~cpu_to_le32((__force u32)crc32); + return cpu_to_le32(~crc32); } diff --git a/net/sctp/input.c b/net/sctp/input.c index 693fd080481..d2e98803ffe 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -83,14 +83,15 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb) { struct sk_buff *list = skb_shinfo(skb)->frag_list; struct sctphdr *sh = sctp_hdr(skb); - __be32 cmp = sh->checksum; - __be32 val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); + __le32 cmp = sh->checksum; + __le32 val; + __u32 tmp = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); for (; list; list = list->next) - val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), - val); + tmp = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), + tmp); - val = sctp_end_cksum(val); + val = sctp_end_cksum(tmp); if (val != cmp) { /* CRC failure, dump it. */ diff --git a/net/sctp/output.c b/net/sctp/output.c index 2d65b7a7330..07d58903a74 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -367,7 +367,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) struct sctp_transport *tp = packet->transport; struct sctp_association *asoc = tp->asoc; struct sctphdr *sh; - __be32 crc32 = cpu_to_be32(0); struct sk_buff *nskb; struct sctp_chunk *chunk, *tmp; struct sock *sk; @@ -532,16 +531,15 @@ int sctp_packet_transmit(struct sctp_packet *packet) * by CRC32-C as described in . */ if (!sctp_checksum_disable && !(dst->dev->features & NETIF_F_NO_CSUM)) { - crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); - crc32 = sctp_end_cksum(crc32); + __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); + + /* 3) Put the resultant value into the checksum field in the + * common header, and leave the rest of the bits unchanged. + */ + sh->checksum = sctp_end_cksum(crc32); } else nskb->ip_summed = CHECKSUM_UNNECESSARY; - /* 3) Put the resultant value into the checksum field in the - * common header, and leave the rest of the bits unchanged. - */ - sh->checksum = crc32; - /* IP layer ECN support * From RFC 2481 * "The ECN-Capable Transport (ECT) bit would be set by the -- cgit v1.2.3-70-g09d2 From faee47cdbfe8d74a1573c2f81ea6dbb08d735be6 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 13 Feb 2009 08:33:43 +0000 Subject: sctp: Fix the RTO-doubling on idle-link heartbeats SCTP incorrectly doubles rto ever time a Hearbeat chunk is generated. However RFC 4960 states: On an idle destination address that is allowed to heartbeat, it is recommended that a HEARTBEAT chunk is sent once per RTO of that destination address plus the protocol parameter 'HB.interval', with jittering of +/- 50% of the RTO value, and exponential backoff of the RTO if the previous HEARTBEAT is unanswered. Essentially, of if the heartbean is unacknowledged, do we double the RTO. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 ++ net/sctp/sm_sideeffect.c | 17 +++++++++++++++-- net/sctp/transport.c | 2 ++ 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 9f70d54fcef..23f08fe1d50 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -910,8 +910,10 @@ struct sctp_transport { * should be set. Every time the RTT * calculation completes (i.e. the DATA chunk * is SACK'd) clear this flag. + * hb_sent : a flag that signals that we have a pending heartbeat. */ __u8 rto_pending; + __u8 hb_sent; /* Flag to track the current fast recovery state */ __u8 fast_recovery; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index e1d6076b4f5..0146cfb1f18 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -461,9 +461,15 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, * expires, set RTO <- RTO * 2 ("back off the timer"). The * maximum value discussed in rule C7 above (RTO.max) may be * used to provide an upper bound to this doubling operation. + * + * Special Case: the first HB doesn't trigger exponential backoff. + * The first unacknowleged HB triggers it. We do this with a flag + * that indicates that we have an outstanding HB. */ - transport->last_rto = transport->rto; - transport->rto = min((transport->rto * 2), transport->asoc->rto_max); + if (transport->hb_sent) { + transport->last_rto = transport->rto; + transport->rto = min((transport->rto * 2), transport->asoc->rto_max); + } } /* Worker routine to handle INIT command failure. */ @@ -621,6 +627,11 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, t->error_count = 0; t->asoc->overall_error_count = 0; + /* Clear the hb_sent flag to signal that we had a good + * acknowledgement. + */ + t->hb_sent = 0; + /* Mark the destination transport address as active if it is not so * marked. */ @@ -657,6 +668,8 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, /* Mark one strike against a transport. */ sctp_do_8_2_transport_strike(asoc, t); + + t->hb_sent = 1; } /* Helper function to process the process SACK command. */ diff --git a/net/sctp/transport.c b/net/sctp/transport.c index e745c118f23..5c29b14ee9a 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -79,6 +79,7 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, peer->rttvar = 0; peer->srtt = 0; peer->rto_pending = 0; + peer->hb_sent = 0; peer->fast_recovery = 0; peer->last_time_heard = jiffies; @@ -608,6 +609,7 @@ void sctp_transport_reset(struct sctp_transport *t) t->flight_size = 0; t->error_count = 0; t->rto_pending = 0; + t->hb_sent = 0; t->fast_recovery = 0; /* Initialize the state information for SFR-CACC */ -- cgit v1.2.3-70-g09d2 From 914e1c8b6980c516667375d3e55f0b6e674c8c58 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 13 Feb 2009 08:33:44 +0000 Subject: sctp: Inherit all socket options from parent correctly. During peeloff/accept() sctp needs to save the parent socket state into the new socket so that any options set on the parent are inherited by the child socket. This was found when the parent/listener socket issues SO_BINDTODEVICE, but the data was misrouted after a route cache flush. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 ++ net/sctp/ipv6.c | 33 +---------------------------- net/sctp/protocol.c | 29 ++------------------------ net/sctp/socket.c | 55 ++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 53 insertions(+), 66 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index bbb7742195b..9e226be3be6 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -138,6 +138,8 @@ void sctp_write_space(struct sock *sk); unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait); void sctp_sock_rfree(struct sk_buff *skb); +void sctp_copy_sock(struct sock *newsk, struct sock *sk, + struct sctp_association *asoc); extern struct percpu_counter sctp_sockets_allocated; /* diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 78622756669..a63de3f7f18 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -627,9 +627,7 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) static struct sock *sctp_v6_create_accept_sk(struct sock *sk, struct sctp_association *asoc) { - struct inet_sock *inet = inet_sk(sk); struct sock *newsk; - struct inet_sock *newinet; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct sctp6_sock *newsctp6sk; @@ -639,17 +637,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, sock_init_data(NULL, newsk); - newsk->sk_type = SOCK_STREAM; - - newsk->sk_prot = sk->sk_prot; - newsk->sk_no_check = sk->sk_no_check; - newsk->sk_reuse = sk->sk_reuse; - - newsk->sk_destruct = inet_sock_destruct; - newsk->sk_family = PF_INET6; - newsk->sk_protocol = IPPROTO_SCTP; - newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; - newsk->sk_shutdown = sk->sk_shutdown; + sctp_copy_sock(newsk, sk, asoc); sock_reset_flag(sk, SOCK_ZAPPED); newsctp6sk = (struct sctp6_sock *)newsk; @@ -657,7 +645,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, sctp_sk(newsk)->v4mapped = sctp_sk(sk)->v4mapped; - newinet = inet_sk(newsk); newnp = inet6_sk(newsk); memcpy(newnp, np, sizeof(struct ipv6_pinfo)); @@ -665,26 +652,8 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk, /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname() * and getpeername(). */ - newinet->sport = inet->sport; - newnp->saddr = np->saddr; - newnp->rcv_saddr = np->rcv_saddr; - newinet->dport = htons(asoc->peer.port); sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk); - /* Init the ipv4 part of the socket since we can have sockets - * using v6 API for ipv4. - */ - newinet->uc_ttl = -1; - newinet->mc_loop = 1; - newinet->mc_ttl = 1; - newinet->mc_index = 0; - newinet->mc_list = NULL; - - if (ipv4_config.no_pmtu_disc) - newinet->pmtudisc = IP_PMTUDISC_DONT; - else - newinet->pmtudisc = IP_PMTUDISC_WANT; - sk_refcnt_debug_inc(newsk); if (newsk->sk_prot->init(newsk)) { diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cc0b592698f..c1e316ee715 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -589,46 +589,21 @@ static int sctp_v4_is_ce(const struct sk_buff *skb) static struct sock *sctp_v4_create_accept_sk(struct sock *sk, struct sctp_association *asoc) { - struct inet_sock *inet = inet_sk(sk); - struct inet_sock *newinet; struct sock *newsk = sk_alloc(sock_net(sk), PF_INET, GFP_KERNEL, sk->sk_prot); + struct inet_sock *newinet; if (!newsk) goto out; sock_init_data(NULL, newsk); - newsk->sk_type = SOCK_STREAM; - - newsk->sk_no_check = sk->sk_no_check; - newsk->sk_reuse = sk->sk_reuse; - newsk->sk_shutdown = sk->sk_shutdown; - - newsk->sk_destruct = inet_sock_destruct; - newsk->sk_family = PF_INET; - newsk->sk_protocol = IPPROTO_SCTP; - newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; + sctp_copy_sock(newsk, sk, asoc); sock_reset_flag(newsk, SOCK_ZAPPED); newinet = inet_sk(newsk); - /* Initialize sk's sport, dport, rcv_saddr and daddr for - * getsockname() and getpeername() - */ - newinet->sport = inet->sport; - newinet->saddr = inet->saddr; - newinet->rcv_saddr = inet->rcv_saddr; - newinet->dport = htons(asoc->peer.port); newinet->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr; - newinet->pmtudisc = inet->pmtudisc; - newinet->id = asoc->next_tsn ^ jiffies; - - newinet->uc_ttl = -1; - newinet->mc_loop = 1; - newinet->mc_ttl = 1; - newinet->mc_index = 0; - newinet->mc_list = NULL; sk_refcnt_debug_inc(newsk); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index ff0a8f88de0..dea864f5de5 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3939,7 +3939,6 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc, { struct sock *sk = asoc->base.sk; struct socket *sock; - struct inet_sock *inetsk; struct sctp_af *af; int err = 0; @@ -3954,18 +3953,18 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc, if (err < 0) return err; - /* Populate the fields of the newsk from the oldsk and migrate the - * asoc to the newsk. - */ - sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH); + sctp_copy_sock(sock->sk, sk, asoc); /* Make peeled-off sockets more like 1-1 accepted sockets. * Set the daddr and initialize id to something more random */ af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family); af->to_sk_daddr(&asoc->peer.primary_addr, sk); - inetsk = inet_sk(sock->sk); - inetsk->id = asoc->next_tsn ^ jiffies; + + /* Populate the fields of the newsk from the oldsk and migrate the + * asoc to the newsk. + */ + sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH); *sockp = sock; @@ -6700,6 +6699,48 @@ done: sctp_skb_set_owner_r(skb, sk); } +void sctp_copy_sock(struct sock *newsk, struct sock *sk, + struct sctp_association *asoc) +{ + struct inet_sock *inet = inet_sk(sk); + struct inet_sock *newinet = inet_sk(newsk); + + newsk->sk_type = sk->sk_type; + newsk->sk_bound_dev_if = sk->sk_bound_dev_if; + newsk->sk_flags = sk->sk_flags; + newsk->sk_no_check = sk->sk_no_check; + newsk->sk_reuse = sk->sk_reuse; + + newsk->sk_shutdown = sk->sk_shutdown; + newsk->sk_destruct = inet_sock_destruct; + newsk->sk_family = sk->sk_family; + newsk->sk_protocol = IPPROTO_SCTP; + newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; + newsk->sk_sndbuf = sk->sk_sndbuf; + newsk->sk_rcvbuf = sk->sk_rcvbuf; + newsk->sk_lingertime = sk->sk_lingertime; + newsk->sk_rcvtimeo = sk->sk_rcvtimeo; + newsk->sk_sndtimeo = sk->sk_sndtimeo; + + newinet = inet_sk(newsk); + + /* Initialize sk's sport, dport, rcv_saddr and daddr for + * getsockname() and getpeername() + */ + newinet->sport = inet->sport; + newinet->saddr = inet->saddr; + newinet->rcv_saddr = inet->rcv_saddr; + newinet->dport = htons(asoc->peer.port); + newinet->pmtudisc = inet->pmtudisc; + newinet->id = asoc->next_tsn ^ jiffies; + + newinet->uc_ttl = inet->uc_ttl; + newinet->mc_loop = 1; + newinet->mc_ttl = 1; + newinet->mc_index = 0; + newinet->mc_list = NULL; +} + /* Populate the fields of the newsk from the oldsk and migrate the assoc * and its messages to the newsk. */ -- cgit v1.2.3-70-g09d2 From e478075c6f07a383c378fb400edc1a7407a941b0 Mon Sep 17 00:00:00 2001 From: Hagen Paul Pfeifer Date: Fri, 20 Feb 2009 10:47:09 +0100 Subject: netfilter: nf_conntrack: table max size should hold at least table size Table size is defined as unsigned, wheres the table maximum size is defined as a signed integer. The calculation of max is 8 or 4, multiplied the table size. Therefore the max value is aligned to unsigned. Signed-off-by: Hagen Paul Pfeifer Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 2 +- net/netfilter/nf_conntrack_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 2e0c53641cb..4dfb793c3f1 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -287,7 +287,7 @@ static inline int nf_ct_is_untracked(const struct sk_buff *skb) extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); extern unsigned int nf_conntrack_htable_size; -extern int nf_conntrack_max; +extern unsigned int nf_conntrack_max; #define NF_CT_STAT_INC(net, count) \ (per_cpu_ptr((net)->ct.stat, raw_smp_processor_id())->count++) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 90ce9ddb945..f3aa4e65b15 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -54,7 +54,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_lock); unsigned int nf_conntrack_htable_size __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); -int nf_conntrack_max __read_mostly; +unsigned int nf_conntrack_max __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_max); struct nf_conn nf_conntrack_untracked __read_mostly; -- cgit v1.2.3-70-g09d2 From ce16c5337ab0d165f95c88aa857207efd7c01139 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 22 Feb 2009 00:11:09 -0800 Subject: netns: Remove net_alive It turns out that net_alive is unnecessary, and the original problem that led to it being added was simply that the icmp code thought it was a network device and wound up being unable to handle packets while there were still packets in the network namespace. Now that icmp and tcp have been fixed to properly register themselves this problem is no longer present and we have a stronger guarantee that packets will not arrive in a network namespace then that provided by net_alive in netif_receive_skb. So remove net_alive allowing packet reception run a little faster. Additionally document the strong reason why network namespace cleanup is safe so that if something happens again someone else will have a chance of figuring it out. Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller --- include/net/net_namespace.h | 27 +++++++++++++++++---------- net/core/dev.c | 6 ------ net/core/net_namespace.c | 3 --- 3 files changed, 17 insertions(+), 19 deletions(-) (limited to 'include/net') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 6fc13d905c5..ded434b032a 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -109,11 +109,6 @@ extern struct list_head net_namespace_list; #ifdef CONFIG_NET_NS extern void __put_net(struct net *net); -static inline int net_alive(struct net *net) -{ - return net && atomic_read(&net->count); -} - static inline struct net *get_net(struct net *net) { atomic_inc(&net->count); @@ -145,11 +140,6 @@ int net_eq(const struct net *net1, const struct net *net2) } #else -static inline int net_alive(struct net *net) -{ - return 1; -} - static inline struct net *get_net(struct net *net) { return net; @@ -234,6 +224,23 @@ struct pernet_operations { void (*exit)(struct net *net); }; +/* + * Use these carefully. If you implement a network device and it + * needs per network namespace operations use device pernet operations, + * otherwise use pernet subsys operations. + * + * This is critically important. Most of the network code cleanup + * runs with the assumption that dev_remove_pack has been called so no + * new packets will arrive during and after the cleanup functions have + * been called. dev_remove_pack is not per namespace so instead the + * guarantee of no more packets arriving in a network namespace is + * provided by ensuring that all network devices and all sockets have + * left the network namespace before the cleanup methods are called. + * + * For the longest time the ipv4 icmp code was registered as a pernet + * device which caused kernel oops, and panics during network + * namespace cleanup. So please don't get this wrong. + */ extern int register_pernet_subsys(struct pernet_operations *); extern void unregister_pernet_subsys(struct pernet_operations *); extern int register_pernet_gen_subsys(int *id, struct pernet_operations *); diff --git a/net/core/dev.c b/net/core/dev.c index 88dc082b47d..ac6ab12d329 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2254,12 +2254,6 @@ int netif_receive_skb(struct sk_buff *skb) rcu_read_lock(); - /* Don't receive packets in an exiting network namespace */ - if (!net_alive(dev_net(skb->dev))) { - kfree_skb(skb); - goto out; - } - #ifdef CONFIG_NET_CLS_ACT if (skb->tc_verd & TC_NCLS) { skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 55151faaf90..516c7b15432 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -140,9 +140,6 @@ static void cleanup_net(struct work_struct *work) struct pernet_operations *ops; struct net *net; - /* Be very certain incoming network packets will not find us */ - rcu_barrier(); - net = container_of(work, struct net, work); mutex_lock(&net_mutex); -- cgit v1.2.3-70-g09d2 From c4f912e155504e94dd4f3d63c378dab0ff03dbda Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:52:16 +0100 Subject: Bluetooth: Add global deferred socket parameter The L2CAP and RFCOMM applications require support for authorization and the ability of rejecting incoming connection requests. The socket interface is not really able to support this. This patch does the ground work for a socket option to defer connection setup. Setting this option allows calling of accept() and then the first read() will trigger the final connection setup. Calling close() would reject the connection. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 3 +++ net/bluetooth/af_bluetooth.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index a04f8463ac7..847e9e6df08 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -53,6 +53,8 @@ #define SOL_SCO 17 #define SOL_RFCOMM 18 +#define BT_DEFER_SETUP 7 + #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) @@ -108,6 +110,7 @@ struct bt_sock { bdaddr_t dst; struct list_head accept_q; struct sock *parent; + u32 defer_setup; }; struct bt_sock_list { diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 744ed3f07ef..7c0031ff8cf 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -217,7 +217,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) continue; } - if (sk->sk_state == BT_CONNECTED || !newsock) { + if (sk->sk_state == BT_CONNECTED || !newsock || + bt_sk(parent)->defer_setup) { bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); @@ -232,7 +233,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) EXPORT_SYMBOL(bt_accept_dequeue); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) + struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -275,6 +276,9 @@ static inline unsigned int bt_accept_poll(struct sock *parent) struct list_head *p, *n; struct sock *sk; + if (bt_sk(parent)->defer_setup) + return POLLIN | POLLRDNORM; + list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); if (sk->sk_state == BT_CONNECTED) -- cgit v1.2.3-70-g09d2 From bb23c0ab824653be4aa7dfca15b07b3059717004 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:56:48 +0100 Subject: Bluetooth: Add support for deferring RFCOMM connection setup In order to decide if listening RFCOMM sockets should be accept()ed the BD_ADDR of the remote device needs to be known. This patch adds a socket option which defines a timeout for deferring the actual connection setup. The connection setup is done after reading from the socket for the first time. Until then writing to the socket returns ENOTCONN. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 6 +++-- net/bluetooth/rfcomm/core.c | 56 +++++++++++++++++++++++++++++++----------- net/bluetooth/rfcomm/sock.c | 44 ++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 18 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 4dc8d92a463..71b45f45968 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -185,6 +185,7 @@ struct rfcomm_dlc { u8 out; u32 link_mode; + u32 defer_setup; uint mtu; uint cfc; @@ -202,10 +203,11 @@ struct rfcomm_dlc { #define RFCOMM_RX_THROTTLED 0 #define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TIMED_OUT 2 -#define RFCOMM_MSC_PENDING 3 +#define RFCOMM_MSC_PENDING 3 #define RFCOMM_AUTH_PENDING 4 #define RFCOMM_AUTH_ACCEPT 5 #define RFCOMM_AUTH_REJECT 6 +#define RFCOMM_DEFER_SETUP 7 /* Scheduling flags and events */ #define RFCOMM_SCHED_STATE 0 @@ -239,6 +241,7 @@ int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); +void rfcomm_dlc_accept(struct rfcomm_dlc *d); #define rfcomm_dlc_lock(d) spin_lock(&d->lock) #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) @@ -333,7 +336,6 @@ struct rfcomm_dev_req { bdaddr_t src; bdaddr_t dst; u8 channel; - }; struct rfcomm_dev_info { diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index acd84fd524b..edee49e00fb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -421,9 +421,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) d, d->state, d->dlci, err, s); switch (d->state) { - case BT_CONNECTED: - case BT_CONFIG: case BT_CONNECT: + case BT_CONFIG: + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + set_bit(RFCOMM_AUTH_REJECT, &d->flags); + rfcomm_schedule(RFCOMM_SCHED_AUTH); + break; + } + /* Fall through */ + + case BT_CONNECTED: d->state = BT_DISCONN; if (skb_queue_empty(&d->tx_queue)) { rfcomm_send_disc(s, d->dlci); @@ -434,6 +441,14 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) } break; + case BT_OPEN: + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + set_bit(RFCOMM_AUTH_REJECT, &d->flags); + rfcomm_schedule(RFCOMM_SCHED_AUTH); + break; + } + /* Fall through */ + default: rfcomm_dlc_clear_timer(d); @@ -1162,7 +1177,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) return 0; } -static void rfcomm_dlc_accept(struct rfcomm_dlc *d) +void rfcomm_dlc_accept(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; @@ -1181,6 +1196,20 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d) rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } +static void rfcomm_check_accept(struct rfcomm_dlc *d) +{ + if (rfcomm_check_link_mode(d)) { + set_bit(RFCOMM_AUTH_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else { + if (d->defer_setup) { + set_bit(RFCOMM_DEFER_SETUP, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else + rfcomm_dlc_accept(d); + } +} + static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; @@ -1203,11 +1232,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) if (d) { if (d->state == BT_OPEN) { /* DLC was previously opened by PN request */ - if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_dlc_accept(d); + rfcomm_check_accept(d); } return 0; } @@ -1219,11 +1244,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) d->addr = __addr(s->initiator, dlci); rfcomm_dlc_link(s, d); - if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_dlc_accept(d); + rfcomm_check_accept(d); } else { rfcomm_send_dm(s, dlci); } @@ -1717,8 +1738,13 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) if (d->out) { rfcomm_send_pn(s, 1, d); rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - } else - rfcomm_dlc_accept(d); + } else { + if (d->defer_setup) { + set_bit(RFCOMM_DEFER_SETUP, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + } else + rfcomm_dlc_accept(d); + } if (d->link_mode & RFCOMM_LM_SECURE) { struct sock *sk = s->sock->sk; hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 65dd7133d72..d37a829a81e 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -262,8 +262,10 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; pi->link_mode = rfcomm_pi(parent)->link_mode; + pi->dlc->defer_setup = bt_sk(parent)->defer_setup; } else { pi->link_mode = 0; + pi->dlc->defer_setup = 0; } pi->dlc->link_mode = pi->link_mode; @@ -554,6 +556,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int sent = 0; + if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) + return -ENOTCONN; + if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; @@ -633,10 +638,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; int err = 0; size_t target, copied = 0; long timeo; + if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { + rfcomm_dlc_accept(d); + return 0; + } + if (flags & MSG_OOB) return -EOPNOTSUPP; @@ -746,6 +757,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c { struct sock *sk = sock->sk; int err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -755,6 +767,20 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + bt_sk(sk)->defer_setup = opt; + break; + default: err = -ENOPROTOOPT; break; @@ -785,7 +811,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u break; case RFCOMM_CONNINFO: - if (sk->sk_state != BT_CONNECTED) { + if (sk->sk_state != BT_CONNECTED && + !rfcomm_pi(sk)->dlc->defer_setup) { err = -ENOTCONN; break; } @@ -826,6 +853,17 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -938,6 +976,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * done: bh_unlock_sock(parent); + + if (bt_sk(parent)->defer_setup) + parent->sk_state_change(parent); + return result; } -- cgit v1.2.3-70-g09d2 From 8c1b235594fbab9a13240a1dac12ea9fd99b6440 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:04 +0100 Subject: Bluetooth: Add enhanced security model for Simple Pairing The current security model is based around the flags AUTH, ENCRYPT and SECURE. Starting with support for the Bluetooth 2.1 specification this is no longer sufficient. The different security levels are now defined as SDP, LOW, MEDIUM and SECURE. Previously it was possible to set each security independently, but this actually doesn't make a lot of sense. For Bluetooth the encryption depends on a previous successful authentication. Also you can only update your existing link key if you successfully created at least one before. And of course the update of link keys without having proper encryption in place is a security issue. The new security levels from the Bluetooth 2.1 specification are now used internally. All old settings are mapped to the new values and this way it ensures that old applications still work. The only limitation is that it is no longer possible to set authentication without also enabling encryption. No application should have done this anyway since this is actually a security issue. Without encryption the integrity of the authentication can't be guaranteed. As default for a new L2CAP or RFCOMM connection, the LOW security level is used. The only exception here are the service discovery sessions on PSM 1 where SDP level is used. To have similar security strength as with a Bluetooth 2.0 and before combination key, the MEDIUM level should be used. This is according to the Bluetooth specification. The MEDIUM level will not require any kind of man-in-the-middle (MITM) protection. Only the HIGH security level will require this. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 9 +++ include/net/bluetooth/hci_core.h | 56 +++++++++------- net/bluetooth/hci_conn.c | 45 ++++++++----- net/bluetooth/hci_event.c | 3 +- net/bluetooth/l2cap.c | 134 ++++++++++++-------------------------- net/bluetooth/rfcomm/core.c | 81 +++++++---------------- net/bluetooth/sco.c | 2 +- 7 files changed, 139 insertions(+), 191 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 847e9e6df08..3ad5390a4dd 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -53,6 +53,15 @@ #define SOL_SCO 17 #define SOL_RFCOMM 18 +#define BT_SECURITY 4 +struct bt_security { + __u8 level; +}; +#define BT_SECURITY_SDP 0 +#define BT_SECURITY_LOW 1 +#define BT_SECURITY_MEDIUM 2 +#define BT_SECURITY_HIGH 3 + #define BT_DEFER_SETUP 7 #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 46a43b721dd..4b14972c169 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -169,6 +169,7 @@ struct hci_conn { __u16 link_policy; __u32 link_mode; __u8 auth_type; + __u8 sec_level; __u8 power_save; unsigned long pend; @@ -325,12 +326,11 @@ int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type); +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); -int hci_conn_auth(struct hci_conn *conn); -int hci_conn_encrypt(struct hci_conn *conn); +int hci_conn_security(struct hci_conn *conn, __u8 sec_level); int hci_conn_change_link_key(struct hci_conn *conn); -int hci_conn_switch_role(struct hci_conn *conn, uint8_t role); +int hci_conn_switch_role(struct hci_conn *conn, __u8 role); void hci_conn_enter_active_mode(struct hci_conn *conn); void hci_conn_enter_sniff_mode(struct hci_conn *conn); @@ -470,26 +470,25 @@ void hci_conn_del_sysfs(struct hci_conn *conn); /* ----- HCI protocols ----- */ struct hci_proto { - char *name; + char *name; unsigned int id; unsigned long flags; void *priv; - int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); + int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); int (*disconn_ind) (struct hci_conn *conn, __u8 reason); int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); - int (*auth_cfm) (struct hci_conn *conn, __u8 status); - int (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); }; static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) { register struct hci_proto *hp; int mask = 0; - + hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->connect_ind) mask |= hp->connect_ind(hdev, bdaddr, type); @@ -530,14 +529,20 @@ static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason) static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) { register struct hci_proto *hp; + __u8 encrypt; + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; hp = hci_proto[HCI_PROTO_L2CAP]; - if (hp && hp->auth_cfm) - hp->auth_cfm(conn, status); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); hp = hci_proto[HCI_PROTO_SCO]; - if (hp && hp->auth_cfm) - hp->auth_cfm(conn, status); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); } static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) @@ -545,12 +550,12 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u register struct hci_proto *hp; hp = hci_proto[HCI_PROTO_L2CAP]; - if (hp && hp->encrypt_cfm) - hp->encrypt_cfm(conn, status, encrypt); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); hp = hci_proto[HCI_PROTO_SCO]; - if (hp && hp->encrypt_cfm) - hp->encrypt_cfm(conn, status, encrypt); + if (hp && hp->security_cfm) + hp->security_cfm(conn, status, encrypt); } int hci_register_proto(struct hci_proto *hproto); @@ -562,8 +567,7 @@ struct hci_cb { char *name; - void (*auth_cfm) (struct hci_conn *conn, __u8 status); - void (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); }; @@ -571,14 +575,20 @@ struct hci_cb { static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { struct list_head *p; + __u8 encrypt; hci_proto_auth_cfm(conn, status); + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return; + + encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + read_lock_bh(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); - if (cb->auth_cfm) - cb->auth_cfm(conn, status); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); } read_unlock_bh(&hci_cb_list_lock); } @@ -592,8 +602,8 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr read_lock_bh(&hci_cb_list_lock); list_for_each(p, &hci_cb_list) { struct hci_cb *cb = list_entry(p, struct hci_cb, list); - if (cb->encrypt_cfm) - cb->encrypt_cfm(conn, status, encrypt); + if (cb->security_cfm) + cb->security_cfm(conn, status, encrypt); } read_unlock_bh(&hci_cb_list_lock); } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a4a789f24c8..98f97a1e9bb 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -325,7 +325,7 @@ EXPORT_SYMBOL(hci_get_route); /* Create SCO or ACL connection. * Device _must_ be locked */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type) +struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) { struct hci_conn *acl; struct hci_conn *sco; @@ -340,6 +340,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 hci_conn_hold(acl); if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { + acl->sec_level = sec_level; acl->auth_type = auth_type; hci_acl_connect(acl); } @@ -385,16 +386,17 @@ int hci_conn_check_link_mode(struct hci_conn *conn) EXPORT_SYMBOL(hci_conn_check_link_mode); /* Authenticate remote device */ -int hci_conn_auth(struct hci_conn *conn) +static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) { BT_DBG("conn %p", conn); - if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) { - if (!(conn->auth_type & 0x01)) { - conn->auth_type |= 0x01; - conn->link_mode &= ~HCI_LM_AUTH; - } - } + if (sec_level > conn->sec_level) + conn->link_mode &= ~HCI_LM_AUTH; + + conn->sec_level = sec_level; + + if (sec_level == BT_SECURITY_HIGH) + conn->auth_type |= 0x01; if (conn->link_mode & HCI_LM_AUTH) return 1; @@ -405,31 +407,42 @@ int hci_conn_auth(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } + return 0; } -EXPORT_SYMBOL(hci_conn_auth); -/* Enable encryption */ -int hci_conn_encrypt(struct hci_conn *conn) +/* Enable security */ +int hci_conn_security(struct hci_conn *conn, __u8 sec_level) { BT_DBG("conn %p", conn); + if (sec_level == BT_SECURITY_SDP) + return 1; + + if (sec_level == BT_SECURITY_LOW) { + if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) + return hci_conn_auth(conn, sec_level); + else + return 1; + } + if (conn->link_mode & HCI_LM_ENCRYPT) - return hci_conn_auth(conn); + return hci_conn_auth(conn, sec_level); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - if (hci_conn_auth(conn)) { + if (hci_conn_auth(conn, sec_level)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } + return 0; } -EXPORT_SYMBOL(hci_conn_encrypt); +EXPORT_SYMBOL(hci_conn_security); /* Change link key */ int hci_conn_change_link_key(struct hci_conn *conn) @@ -442,12 +455,13 @@ int hci_conn_change_link_key(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); } + return 0; } EXPORT_SYMBOL(hci_conn_change_link_key); /* Switch role */ -int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) +int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("conn %p", conn); @@ -460,6 +474,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) cp.role = role; hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); } + return 0; } EXPORT_SYMBOL(hci_conn_switch_role); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index beea9dbb656..014fc8b320b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1601,7 +1601,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && - conn->ssp_mode > 0 && conn->out) { + conn->ssp_mode > 0 && conn->out && + conn->sec_level != BT_SECURITY_SDP) { struct hci_cp_auth_requested cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 123efb46d3f..eadf0923186 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -263,12 +263,17 @@ static inline int l2cap_check_link_mode(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) || - (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)) - return hci_conn_encrypt(conn->hcon); + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); + + if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) - return hci_conn_auth(conn->hcon); + return hci_conn_security(conn->hcon, BT_SECURITY_LOW); + + if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) + return hci_conn_security(conn->hcon, BT_SECURITY_SDP); return 1; } @@ -803,6 +808,7 @@ static int l2cap_do_connect(struct sock *sk) struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; + __u8 sec_level; __u8 auth_type; int err = 0; @@ -815,21 +821,37 @@ static int l2cap_do_connect(struct sock *sk) err = -ENOMEM; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH || - l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT || - l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) { - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - auth_type = HCI_AT_NO_BONDING_MITM; + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + sec_level = BT_SECURITY_HIGH; + else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) + sec_level = BT_SECURITY_SDP; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + sec_level = BT_SECURITY_MEDIUM; + else + sec_level = BT_SECURITY_LOW; + + if (sk->sk_type == SOCK_RAW) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + auth_type = HCI_AT_DEDICATED_BONDING; else - auth_type = HCI_AT_GENERAL_BONDING_MITM; - } else { - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) auth_type = HCI_AT_NO_BONDING; + } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_NO_BONDING_MITM; else + auth_type = HCI_AT_NO_BONDING; + } else { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + auth_type = HCI_AT_GENERAL_BONDING_MITM; + else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) auth_type = HCI_AT_GENERAL_BONDING; + else + auth_type = HCI_AT_NO_BONDING; } - hcon = hci_connect(hdev, ACL_LINK, dst, auth_type); + hcon = hci_connect(hdev, ACL_LINK, dst, sec_level, auth_type); if (!hcon) goto done; @@ -1402,11 +1424,6 @@ static void l2cap_chan_ready(struct sock *sk) */ parent->sk_data_ready(parent, 0); } - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - hci_conn_change_link_key(conn->hcon); - } } /* Copy frame to all raw sockets on that connection */ @@ -2323,77 +2340,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) return 0; } -static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) -{ - struct l2cap_chan_list *l; - struct l2cap_conn *conn = hcon->l2cap_data; - struct sock *sk; - - if (!conn) - return 0; - - l = &conn->chan_list; - - BT_DBG("conn %p", conn); - - read_lock(&l->lock); - - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - - bh_lock_sock(sk); - - if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) && - !(hcon->link_mode & HCI_LM_ENCRYPT) && - !status) { - bh_unlock_sock(sk); - continue; - } - - if (sk->sk_state == BT_CONNECT) { - if (!status) { - struct l2cap_conn_req req; - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; - - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_REQ, sizeof(req), &req); - } else { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 10); - } - } else if (sk->sk_state == BT_CONNECT2) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (!status) { - sk->sk_state = BT_CONFIG; - result = L2CAP_CR_SUCCESS; - } else { - sk->sk_state = BT_DISCONN; - l2cap_sock_set_timer(sk, HZ / 10); - result = L2CAP_CR_SEC_BLOCK; - } - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - } - - bh_unlock_sock(sk); - } - - read_unlock(&l->lock); - - return 0; -} - -static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) +static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { struct l2cap_chan_list *l; struct l2cap_conn *conn = hcon->l2cap_data; @@ -2413,10 +2360,10 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) bh_lock_sock(sk); - if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) && + if (!status && encrypt == 0x00 && + (pi->link_mode & L2CAP_LM_SECURE) && (sk->sk_state == BT_CONNECTED || - sk->sk_state == BT_CONFIG) && - !status && encrypt == 0x00) { + sk->sk_state == BT_CONFIG)) { __l2cap_sock_close(sk, ECONNREFUSED); bh_unlock_sock(sk); continue; @@ -2608,8 +2555,7 @@ static struct hci_proto l2cap_hci_proto = { .connect_ind = l2cap_connect_ind, .connect_cfm = l2cap_connect_cfm, .disconn_ind = l2cap_disconn_ind, - .auth_cfm = l2cap_auth_cfm, - .encrypt_cfm = l2cap_encrypt_cfm, + .security_cfm = l2cap_security_cfm, .recv_acldata = l2cap_recv_acldata }; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index edee49e00fb..68f70c5270c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -226,16 +226,18 @@ static int rfcomm_l2sock_create(struct socket **sock) static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) { - if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon)) - return 1; - } else if (d->link_mode & RFCOMM_LM_AUTH) { - if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon)) - return 1; - } + if (d->link_mode & RFCOMM_LM_SECURE) + return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - return 0; + if (d->link_mode & RFCOMM_LM_ENCRYPT) + return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); + + if (d->link_mode & RFCOMM_LM_AUTH) + return hci_conn_security(conn->hcon, BT_SECURITY_LOW); + + return 1; } /* ---- RFCOMM DLCs ---- */ @@ -389,9 +391,9 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, if (s->state == BT_CONNECTED) { if (rfcomm_check_link_mode(d)) - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - else rfcomm_send_pn(s, 1, d); + else + set_bit(RFCOMM_AUTH_PENDING, &d->flags); } rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); @@ -1199,14 +1201,14 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) static void rfcomm_check_accept(struct rfcomm_dlc *d) { if (rfcomm_check_link_mode(d)) { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } else rfcomm_dlc_accept(d); + } else { + set_bit(RFCOMM_AUTH_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); } } @@ -1659,10 +1661,11 @@ static void rfcomm_process_connect(struct rfcomm_session *s) if (d->state == BT_CONFIG) { d->mtu = s->mtu; if (rfcomm_check_link_mode(d)) { + rfcomm_send_pn(s, 1, d); + } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } else - rfcomm_send_pn(s, 1, d); + } } } } @@ -1973,42 +1976,7 @@ static int rfcomm_run(void *unused) return 0; } -static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status) -{ - struct rfcomm_session *s; - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("conn %p status 0x%02x", conn, status); - - s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); - if (!s) - return; - - rfcomm_session_hold(s); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) && - !(conn->link_mode & HCI_LM_ENCRYPT) && !status) - continue; - - if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) - continue; - - if (!status) - set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); - else - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - } - - rfcomm_session_put(s); - - rfcomm_schedule(RFCOMM_SCHED_AUTH); -} - -static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) +static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) { struct rfcomm_session *s; struct rfcomm_dlc *d; @@ -2025,10 +1993,10 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); - if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) && + if (!status && encrypt == 0x00 && + (d->link_mode & RFCOMM_LM_ENCRYPT) && (d->state == BT_CONNECTED || - d->state == BT_CONFIG) && - !status && encrypt == 0x00) { + d->state == BT_CONFIG)) { __rfcomm_dlc_close(d, ECONNREFUSED); continue; } @@ -2036,7 +2004,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) continue; - if (!status && encrypt) + if (!status) set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); else set_bit(RFCOMM_AUTH_REJECT, &d->flags); @@ -2049,8 +2017,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt) static struct hci_cb rfcomm_cb = { .name = "RFCOMM", - .auth_cfm = rfcomm_auth_cfm, - .encrypt_cfm = rfcomm_encrypt_cfm + .security_cfm = rfcomm_security_cfm }; static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 71df982c09c..7f10f97cd69 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -195,7 +195,7 @@ static int sco_connect(struct sock *sk) else type = SCO_LINK; - hcon = hci_connect(hdev, type, dst, HCI_AT_NO_BONDING); + hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (!hcon) goto done; -- cgit v1.2.3-70-g09d2 From 2af6b9d518ddfbc4d6990d5f9c9b1a05341c1cef Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:38 +0100 Subject: Bluetooth: Replace L2CAP link mode with security level Change the L2CAP internals to use the new security levels and remove the link mode details. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 5 +- net/bluetooth/l2cap.c | 160 +++++++++++++++++++++++++++++------------- 2 files changed, 113 insertions(+), 52 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 73e115bc12d..29f720e6188 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -237,8 +237,9 @@ struct l2cap_pinfo { __u16 imtu; __u16 omtu; __u16 flush_to; - - __u32 link_mode; + __u8 sec_level; + __u8 role_switch; + __u8 force_reliable; __u8 conf_req[64]; __u8 conf_len; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index eadf0923186..e899a9371c0 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -78,8 +78,7 @@ static void l2cap_sock_timeout(unsigned long arg) bh_lock_sock(sk); if (sk->sk_state == BT_CONNECT && - (l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH | - L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE))) + l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; @@ -259,23 +258,11 @@ static void l2cap_chan_del(struct sock *sk, int err) } /* Service level security */ -static inline int l2cap_check_link_mode(struct sock *sk) +static inline int l2cap_check_security(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) - return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) - return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); - - if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) - return hci_conn_security(conn->hcon, BT_SECURITY_LOW); - - if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - return hci_conn_security(conn->hcon, BT_SECURITY_SDP); - - return 1; + return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level); } static inline u8 l2cap_get_ident(struct l2cap_conn *conn) @@ -317,7 +304,7 @@ static void l2cap_do_start(struct sock *sk) struct l2cap_conn *conn = l2cap_pi(sk)->conn; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; @@ -361,7 +348,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } if (sk->sk_state == BT_CONNECT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; @@ -376,7 +363,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { struct sock *parent = bt_sk(sk)->parent; rsp.result = cpu_to_le16(L2CAP_CR_PEND); @@ -439,7 +426,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) + if (l2cap_pi(sk)->force_reliable) sk->sk_err = err; } @@ -690,11 +677,15 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; - pi->link_mode = l2cap_pi(parent)->link_mode; + pi->sec_level = l2cap_pi(parent)->sec_level; + pi->role_switch = l2cap_pi(parent)->role_switch; + pi->force_reliable = l2cap_pi(parent)->force_reliable; } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; - pi->link_mode = 0; + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; + pi->force_reliable = 0; } /* Default config options */ @@ -792,6 +783,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ l2cap_pi(sk)->psm = la->l2_psm; l2cap_pi(sk)->sport = la->l2_psm; sk->sk_state = BT_BOUND; + + if (btohs(la->l2_psm) == 0x0001) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } write_unlock_bh(&l2cap_sk_list.lock); @@ -808,7 +802,6 @@ static int l2cap_do_connect(struct sock *sk) struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; - __u8 sec_level; __u8 auth_type; int err = 0; @@ -821,37 +814,39 @@ static int l2cap_do_connect(struct sock *sk) err = -ENOMEM; - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) - sec_level = BT_SECURITY_HIGH; - else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) - sec_level = BT_SECURITY_SDP; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) - sec_level = BT_SECURITY_MEDIUM; - else - sec_level = BT_SECURITY_LOW; - if (sk->sk_type == SOCK_RAW) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: auth_type = HCI_AT_DEDICATED_BONDING_MITM; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + break; + case BT_SECURITY_MEDIUM: auth_type = HCI_AT_DEDICATED_BONDING; - else + break; + default: auth_type = HCI_AT_NO_BONDING; + break; + } } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) auth_type = HCI_AT_NO_BONDING_MITM; else auth_type = HCI_AT_NO_BONDING; } else { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: auth_type = HCI_AT_GENERAL_BONDING_MITM; - else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) + break; + case BT_SECURITY_MEDIUM: auth_type = HCI_AT_GENERAL_BONDING; - else + break; + default: auth_type = HCI_AT_NO_BONDING; + break; + } } - hcon = hci_connect(hdev, ACL_LINK, dst, sec_level, auth_type); + hcon = hci_connect(hdev, ACL_LINK, dst, + l2cap_pi(sk)->sec_level, auth_type); if (!hcon) goto done; @@ -1219,7 +1214,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - l2cap_pi(sk)->link_mode = opt; + if (opt & L2CAP_LM_AUTH) + l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & L2CAP_LM_ENCRYPT) + l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & L2CAP_LM_SECURE) + l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + + l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); + l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); break; default: @@ -1234,7 +1237,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; - int err = 0; + struct bt_security sec; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); @@ -1245,6 +1249,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level < BT_SECURITY_LOW || + sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->sec_level = sec.level; + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -1274,6 +1296,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -1296,7 +1319,29 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case L2CAP_LM: - if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval)) + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = L2CAP_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (l2cap_pi(sk)->role_switch) + opt |= L2CAP_LM_MASTER; + + if (l2cap_pi(sk)->force_reliable) + opt |= L2CAP_LM_RELIABLE; + + if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; @@ -1329,6 +1374,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct bt_security sec; int len, err = 0; BT_DBG("sk %p", sk); @@ -1342,6 +1388,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = l2cap_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -1771,7 +1826,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (l2cap_check_link_mode(sk)) { + if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONNECT2; result = L2CAP_CR_PEND; @@ -2299,10 +2354,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { - lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); + lm1 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm1 |= HCI_LM_MASTER; exact++; - } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); + } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + lm2 |= HCI_LM_ACCEPT; + if (l2cap_pi(sk)->role_switch) + lm2 |= HCI_LM_MASTER; + } } read_unlock(&l2cap_sk_list.lock); @@ -2361,7 +2421,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) bh_lock_sock(sk); if (!status && encrypt == 0x00 && - (pi->link_mode & L2CAP_LM_SECURE) && + pi->sec_level == BT_SECURITY_HIGH && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { __l2cap_sock_close(sk, ECONNREFUSED); @@ -2510,10 +2570,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf) sk_for_each(sk, node, &l2cap_sk_list.head) { struct l2cap_pinfo *pi = l2cap_pi(sk); - str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n", + str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid, - pi->imtu, pi->omtu, pi->link_mode); + pi->imtu, pi->omtu, pi->sec_level); } read_unlock_bh(&l2cap_sk_list.lock); -- cgit v1.2.3-70-g09d2 From 9f2c8a03fbb3048cf38b158f87aa0c3c09bca084 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Jan 2009 21:58:40 +0100 Subject: Bluetooth: Replace RFCOMM link mode with security level Change the RFCOMM internals to use the new security levels and remove the link mode details. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 7 ++-- net/bluetooth/rfcomm/core.c | 28 ++++------------ net/bluetooth/rfcomm/sock.c | 75 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 79 insertions(+), 31 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 71b45f45968..bda68d833dd 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -183,8 +183,8 @@ struct rfcomm_dlc { u8 remote_v24_sig; u8 mscex; u8 out; - - u32 link_mode; + u8 sec_level; + u8 role_switch; u32 defer_setup; uint mtu; @@ -307,7 +307,8 @@ struct rfcomm_pinfo { struct bt_sock bt; struct rfcomm_dlc *dlc; u8 channel; - u32 link_mode; + u8 sec_level; + u8 role_switch; }; int rfcomm_init_sockets(void); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 68f70c5270c..db83f92d274 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -223,21 +223,11 @@ static int rfcomm_l2sock_create(struct socket **sock) return err; } -static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d) +static inline int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - if (d->link_mode & RFCOMM_LM_SECURE) - return hci_conn_security(conn->hcon, BT_SECURITY_HIGH); - - if (d->link_mode & RFCOMM_LM_ENCRYPT) - return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM); - - if (d->link_mode & RFCOMM_LM_AUTH) - return hci_conn_security(conn->hcon, BT_SECURITY_LOW); - - return 1; + return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level); } /* ---- RFCOMM DLCs ---- */ @@ -390,7 +380,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; if (s->state == BT_CONNECTED) { - if (rfcomm_check_link_mode(d)) + if (rfcomm_check_security(d)) rfcomm_send_pn(s, 1, d); else set_bit(RFCOMM_AUTH_PENDING, &d->flags); @@ -1192,7 +1182,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) d->state_change(d, 0); rfcomm_dlc_unlock(d); - if (d->link_mode & RFCOMM_LM_MASTER) + if (d->role_switch) hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00); rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); @@ -1200,7 +1190,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) static void rfcomm_check_accept(struct rfcomm_dlc *d) { - if (rfcomm_check_link_mode(d)) { + if (rfcomm_check_security(d)) { if (d->defer_setup) { set_bit(RFCOMM_DEFER_SETUP, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); @@ -1660,7 +1650,7 @@ static void rfcomm_process_connect(struct rfcomm_session *s) d = list_entry(p, struct rfcomm_dlc, list); if (d->state == BT_CONFIG) { d->mtu = s->mtu; - if (rfcomm_check_link_mode(d)) { + if (rfcomm_check_security(d)) { rfcomm_send_pn(s, 1, d); } else { set_bit(RFCOMM_AUTH_PENDING, &d->flags); @@ -1748,10 +1738,6 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) } else rfcomm_dlc_accept(d); } - if (d->link_mode & RFCOMM_LM_SECURE) { - struct sock *sk = s->sock->sk; - hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon); - } continue; } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { rfcomm_dlc_clear_timer(d); @@ -1994,7 +1980,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) d = list_entry(p, struct rfcomm_dlc, list); if (!status && encrypt == 0x00 && - (d->link_mode & RFCOMM_LM_ENCRYPT) && + d->sec_level == BT_SECURITY_HIGH && (d->state == BT_CONNECTED || d->state == BT_CONFIG)) { __rfcomm_dlc_close(d, ECONNREFUSED); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index d37a829a81e..9986ef35c89 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -261,14 +261,19 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; - pi->link_mode = rfcomm_pi(parent)->link_mode; pi->dlc->defer_setup = bt_sk(parent)->defer_setup; + + pi->sec_level = rfcomm_pi(parent)->sec_level; + pi->role_switch = rfcomm_pi(parent)->role_switch; } else { - pi->link_mode = 0; pi->dlc->defer_setup = 0; + + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; } - pi->dlc->link_mode = pi->link_mode; + pi->dlc->sec_level = pi->sec_level; + pi->dlc->role_switch = pi->role_switch; } static struct proto rfcomm_proto = { @@ -408,7 +413,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; - d->link_mode = rfcomm_pi(sk)->link_mode; + d->sec_level = rfcomm_pi(sk)->sec_level; + d->role_switch = rfcomm_pi(sk)->role_switch; err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); if (!err) @@ -741,7 +747,14 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u break; } - rfcomm_pi(sk)->link_mode = opt; + if (opt & RFCOMM_LM_AUTH) + rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & RFCOMM_LM_ENCRYPT) + rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & RFCOMM_LM_SECURE) + rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH; + + rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER); break; default: @@ -756,7 +769,8 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; - int err = 0; + struct bt_security sec; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); @@ -767,6 +781,23 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + rfcomm_pi(sk)->sec_level = sec.level; + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; @@ -796,6 +827,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u struct sock *l2cap_sk; struct rfcomm_conninfo cinfo; int len, err = 0; + u32 opt; BT_DBG("sk %p", sk); @@ -806,7 +838,26 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u switch (optname) { case RFCOMM_LM: - if (put_user(rfcomm_pi(sk)->link_mode, (u32 __user *) optval)) + switch (rfcomm_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = RFCOMM_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | + RFCOMM_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (rfcomm_pi(sk)->role_switch) + opt |= RFCOMM_LM_MASTER; + + if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; break; @@ -840,6 +891,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct bt_security sec; int len, err = 0; BT_DBG("sk %p", sk); @@ -853,6 +905,15 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c lock_sock(sk); switch (optname) { + case BT_SECURITY: + sec.level = rfcomm_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + case BT_DEFER_SETUP: if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { err = -EINVAL; -- cgit v1.2.3-70-g09d2 From 8c84b83076b5062f59b6167cdda90d9e5124aa71 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 16 Jan 2009 08:17:51 +0100 Subject: Bluetooth: Pause RFCOMM TX when encryption drops A role switch with devices following the Bluetooth pre-2.1 standards or without Encryption Pause and Resume support is not possible if encryption is enabled. Most newer headsets require the role switch, but also require that the connection is encrypted. For connections with a high security mode setting, the link will be immediately dropped. When the connection uses medium security mode setting, then a grace period is introduced where the TX is halted and the remote device gets a change to re-enable encryption after the role switch. If not re-enabled the link will be dropped. Based on initial work by Ville Tervo Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 9 +++++---- net/bluetooth/rfcomm/core.c | 23 +++++++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index bda68d833dd..80072611d26 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -204,10 +204,11 @@ struct rfcomm_dlc { #define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TIMED_OUT 2 #define RFCOMM_MSC_PENDING 3 -#define RFCOMM_AUTH_PENDING 4 -#define RFCOMM_AUTH_ACCEPT 5 -#define RFCOMM_AUTH_REJECT 6 -#define RFCOMM_DEFER_SETUP 7 +#define RFCOMM_SEC_PENDING 4 +#define RFCOMM_AUTH_PENDING 5 +#define RFCOMM_AUTH_ACCEPT 6 +#define RFCOMM_AUTH_REJECT 7 +#define RFCOMM_DEFER_SETUP 8 /* Scheduling flags and events */ #define RFCOMM_SCHED_STATE 0 diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index db83f92d274..dafaee91cdf 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1979,12 +1979,23 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); - if (!status && encrypt == 0x00 && - d->sec_level == BT_SECURITY_HIGH && - (d->state == BT_CONNECTED || - d->state == BT_CONFIG)) { - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; + if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { + rfcomm_dlc_clear_timer(d); + if (status || encrypt == 0x00) { + __rfcomm_dlc_close(d, ECONNREFUSED); + continue; + } + } + + if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { + if (d->sec_level == BT_SECURITY_MEDIUM) { + set_bit(RFCOMM_SEC_PENDING, &d->flags); + rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); + continue; + } else if (d->sec_level == BT_SECURITY_HIGH) { + __rfcomm_dlc_close(d, ECONNREFUSED); + continue; + } } if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) -- cgit v1.2.3-70-g09d2 From efc7688b557dd1be10eead7399b315efcb1dbc74 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 09:13:37 +0100 Subject: Bluetooth: Add SCO fallback for eSCO connection attempts When attempting to setup eSCO connections it can happen that some link manager implementations fail to properly negotiate the eSCO parameters and thus fail the eSCO setup. Normally the link manager is responsible for the negotiation of the parameters and actually fallback to SCO if no agreement can be reached. In cases where the link manager is just too stupid, then at least try to establish a SCO link if eSCO fails. For the Bluetooth devices with EDR support this includes handling packet types of EDR basebands. This is particular tricky since for the EDR the logic of enabling/disabling one specific packet type is turned around. This fix contains an extra bitmask to disable eSCO EDR packet when trying to fallback to a SCO connection. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 8 ++++++++ net/bluetooth/hci_conn.c | 9 +++++++-- net/bluetooth/hci_event.c | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3645139e68c..f69f015bbcc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -133,8 +133,13 @@ enum { #define ESCO_EV3 0x0008 #define ESCO_EV4 0x0010 #define ESCO_EV5 0x0020 +#define ESCO_2EV3 0x0040 +#define ESCO_3EV3 0x0080 +#define ESCO_2EV5 0x0100 +#define ESCO_3EV5 0x0200 #define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3) +#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) /* ACL flags */ #define ACL_CONT 0x01 @@ -176,6 +181,9 @@ enum { #define LMP_EV5 0x02 #define LMP_SNIFF_SUBR 0x02 +#define LMP_EDR_ESCO_2M 0x20 +#define LMP_EDR_ESCO_3M 0x40 +#define LMP_EDR_3S_ESCO 0x80 #define LMP_SIMPLE_PAIR 0x08 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 98f97a1e9bb..2435e830ba6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -123,6 +123,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) conn->state = BT_CONNECT; conn->out = 1; + conn->attempt++; + cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -139,6 +141,8 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) conn->state = BT_CONNECT; conn->out = 1; + conn->attempt++; + cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -216,12 +220,13 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) break; case SCO_LINK: if (lmp_esco_capable(hdev)) - conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK; + conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | + (hdev->esco_type & EDR_ESCO_MASK); else conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; break; case ESCO_LINK: - conn->pkt_type = hdev->esco_type; + conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; break; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 014fc8b320b..899b8991a46 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -484,6 +484,15 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb if (hdev->features[4] & LMP_EV5) hdev->esco_type |= (ESCO_EV5); + if (hdev->features[5] & LMP_EDR_ESCO_2M) + hdev->esco_type |= (ESCO_2EV3); + + if (hdev->features[5] & LMP_EDR_ESCO_3M) + hdev->esco_type |= (ESCO_3EV3); + + if (hdev->features[5] & LMP_EDR_3S_ESCO) + hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5); + BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, hdev->features[0], hdev->features[1], hdev->features[2], hdev->features[3], @@ -1639,6 +1648,13 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu conn->type = SCO_LINK; } + if (conn->out && ev->status == 0x1c && conn->attempt < 2) { + conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | + (hdev->esco_type & EDR_ESCO_MASK); + hci_setup_sync(conn, conn->link->handle); + goto unlock; + } + if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; -- cgit v1.2.3-70-g09d2 From 0684e5f9fb9e3f7e168ab831dfca693bcb44805b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 02:48:38 +0100 Subject: Bluetooth: Use general bonding whenever possible When receiving incoming connection to specific services, always use general bonding. This ensures that the link key gets stored and can be used for further authentications. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 23 +++++++++-------------- net/bluetooth/l2cap.c | 16 +++++++++++++++- net/bluetooth/rfcomm/core.c | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 17 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4b14972c169..f75028b3388 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -328,7 +328,7 @@ void hci_conn_check_pending(struct hci_dev *hdev); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); -int hci_conn_security(struct hci_conn *conn, __u8 sec_level); +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, __u8 role); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 2435e830ba6..7fc4c048b57 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -391,19 +391,14 @@ int hci_conn_check_link_mode(struct hci_conn *conn) EXPORT_SYMBOL(hci_conn_check_link_mode); /* Authenticate remote device */ -static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) +static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); - if (sec_level > conn->sec_level) - conn->link_mode &= ~HCI_LM_AUTH; - - conn->sec_level = sec_level; - - if (sec_level == BT_SECURITY_HIGH) - conn->auth_type |= 0x01; - - if (conn->link_mode & HCI_LM_AUTH) + if (sec_level > conn->sec_level) { + conn->sec_level = sec_level; + conn->auth_type = auth_type; + } else if (conn->link_mode & HCI_LM_AUTH) return 1; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { @@ -417,7 +412,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level) } /* Enable security */ -int hci_conn_security(struct hci_conn *conn, __u8 sec_level) +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); @@ -426,18 +421,18 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level) if (sec_level == BT_SECURITY_LOW) { if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) - return hci_conn_auth(conn, sec_level); + return hci_conn_auth(conn, sec_level, auth_type); else return 1; } if (conn->link_mode & HCI_LM_ENCRYPT) - return hci_conn_auth(conn, sec_level); + return hci_conn_auth(conn, sec_level, auth_type); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - if (hci_conn_auth(conn, sec_level)) { + if (hci_conn_auth(conn, sec_level, auth_type)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b677af671f3..8a93dde4095 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -263,8 +263,22 @@ static void l2cap_chan_del(struct sock *sk, int err) static inline int l2cap_check_security(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; + __u8 auth_type; + + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } - return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level); + return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level, + auth_type); } static inline u8 l2cap_get_ident(struct l2cap_conn *conn) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 3717c25ba33..1828ec06ad1 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -226,8 +226,22 @@ static int rfcomm_l2sock_create(struct socket **sock) static inline int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + __u8 auth_type; - return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level); + switch (d->sec_level) { + case BT_SECURITY_HIGH: + auth_type = HCI_AT_GENERAL_BONDING_MITM; + break; + case BT_SECURITY_MEDIUM: + auth_type = HCI_AT_GENERAL_BONDING; + break; + default: + auth_type = HCI_AT_NO_BONDING; + break; + } + + return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level, + auth_type); } /* ---- RFCOMM DLCs ---- */ -- cgit v1.2.3-70-g09d2 From 984947dc64f82bc6cafa4d84ba1a139718f634a8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 23:35:19 +0100 Subject: Bluetooth: Fix race condition with L2CAP information request When two L2CAP connections are requested quickly after the ACL link has been established there exists a window for a race condition where a connection request is sent before the information response has been received. Any connection request should only be sent after an exchange of the extended features mask has been finished. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap.c | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 29f720e6188..1c8cf3e9b1c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -223,7 +223,8 @@ struct l2cap_conn { }; #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 -#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02 +#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 +#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 /* ----- L2CAP channel and socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8a93dde4095..07fdbc7dd54 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -320,6 +320,9 @@ static void l2cap_do_start(struct sock *sk) struct l2cap_conn *conn = l2cap_pi(sk)->conn; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { + if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) + return; + if (l2cap_check_security(sk)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(l2cap_pi(sk)->scid); @@ -455,6 +458,8 @@ static void l2cap_info_timeout(unsigned long arg) conn->info_ident = 0; + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + l2cap_conn_start(conn); } @@ -1787,6 +1792,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd cmd->ident == conn->info_ident) { conn->info_ident = 0; del_timer(&conn->info_timer); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + l2cap_conn_start(conn); } @@ -1857,7 +1865,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->ident = cmd->ident; - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_check_security(sk)) { if (bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONNECT2; @@ -2176,10 +2184,13 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm del_timer(&conn->info_timer); - if (type == L2CAP_IT_FEAT_MASK) + if (type == L2CAP_IT_FEAT_MASK) { conn->feat_mask = get_unaligned_le32(rsp->data); - l2cap_conn_start(conn); + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + + l2cap_conn_start(conn); + } return 0; } -- cgit v1.2.3-70-g09d2 From 6a8d3010b313d99adbb28f1826fac0234395bb26 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Feb 2009 23:56:36 +0100 Subject: Bluetooth: Fix double L2CAP connection request If the remote L2CAP server uses authentication pending stage and encryption is enabled it can happen that a L2CAP connection request is sent twice due to a race condition in the connection state machine. When the remote side indicates any kind of connection pending, then track this state and skip sending of L2CAP commands for this period. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1c8cf3e9b1c..4781d285b2e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -259,6 +259,7 @@ struct l2cap_pinfo { #define L2CAP_CONF_REQ_SENT 0x01 #define L2CAP_CONF_INPUT_DONE 0x02 #define L2CAP_CONF_OUTPUT_DONE 0x04 +#define L2CAP_CONF_CONNECT_PEND 0x80 #define L2CAP_CONF_MAX_RETRIES 2 diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 07fdbc7dd54..01f750142d5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1946,11 +1946,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_pi(sk)->dcid = dcid; l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); break; case L2CAP_CR_PEND: + l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; break; default: @@ -2478,6 +2481,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) { + bh_unlock_sock(sk); + continue; + } + if (!status && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { l2cap_check_encryption(sk, encrypt); -- cgit v1.2.3-70-g09d2 From 435fef20acfc48f46476abad55b0cd3aa47b8365 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 03:55:28 +0100 Subject: Bluetooth: Don't enforce authentication for L2CAP PSM 1 and 3 The recommendation for the L2CAP PSM 1 (SDP) is to not use any kind of authentication or encryption. So don't trigger authentication for incoming and outgoing SDP connections. For L2CAP PSM 3 (RFCOMM) there is no clear requirement, but with Bluetooth 2.1 the initiator is required to enable authentication and encryption first and this gets enforced. So there is no need to trigger an additional authentication step. The RFCOMM service security will make sure that a secure enough link key is present. When the encryption gets enabled after the SDP connection setup, then switch the security level from SDP to low security. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/l2cap.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f75028b3388..9473fce499e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -597,6 +597,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr { struct list_head *p; + if (conn->sec_level == BT_SECURITY_SDP) + conn->sec_level = BT_SECURITY_LOW; + hci_proto_encrypt_cfm(conn, status, encrypt); read_lock_bh(&hci_cb_list_lock); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 01f750142d5..88340d24d11 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -805,7 +805,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ l2cap_pi(sk)->sport = la->l2_psm; sk->sk_state = BT_BOUND; - if (btohs(la->l2_psm) == 0x0001) + if (btohs(la->l2_psm) == 0x0001 || btohs(la->l2_psm) == 0x0003) l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } @@ -852,6 +852,9 @@ static int l2cap_do_connect(struct sock *sk) auth_type = HCI_AT_NO_BONDING_MITM; else auth_type = HCI_AT_NO_BONDING; + + if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } else { switch (l2cap_pi(sk)->sec_level) { case BT_SECURITY_HIGH: -- cgit v1.2.3-70-g09d2 From e1027a7c69700301d14db03d2e049ee60c4f92df Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Feb 2009 09:18:02 +0100 Subject: Bluetooth: Request L2CAP fixed channel list if available If the extended features mask indicates support for fixed channels, request the list of available fixed channels. This also enables the fixed channel features bit so remote implementations can request information about it. Currently only the signal channel will be listed. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4781d285b2e..abfec884771 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -185,6 +185,7 @@ struct l2cap_info_rsp { /* info type */ #define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_FIXED_CHAN 0x0003 /* info result */ #define L2CAP_IR_SUCCESS 0x0000 diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 88340d24d11..985366c36f4 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -50,9 +50,10 @@ #include #include -#define VERSION "2.12" +#define VERSION "2.13" -static u32 l2cap_feat_mask = 0x0000; +static u32 l2cap_feat_mask = 0x0080; +static u8 l2cap_fixed_chan[8] = { 0x02, }; static const struct proto_ops l2cap_sock_ops; @@ -456,9 +457,8 @@ static void l2cap_info_timeout(unsigned long arg) { struct l2cap_conn *conn = (void *) arg; - conn->info_ident = 0; - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -1793,10 +1793,10 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && cmd->ident == conn->info_ident) { - conn->info_ident = 0; del_timer(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -2165,6 +2165,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); + } else if (type == L2CAP_IT_FIXED_CHAN) { + u8 buf[12]; + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); + memcpy(buf + 4, l2cap_fixed_chan, 8); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(buf), buf); } else { struct l2cap_info_rsp rsp; rsp.type = cpu_to_le16(type); @@ -2186,14 +2194,28 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); - conn->info_ident = 0; - del_timer(&conn->info_timer); if (type == L2CAP_IT_FEAT_MASK) { conn->feat_mask = get_unaligned_le32(rsp->data); + if (conn->feat_mask & 0x0080) { + struct l2cap_info_req req; + req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); + + conn->info_ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(req), &req); + } else { + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; + + l2cap_conn_start(conn); + } + } else if (type == L2CAP_IT_FIXED_CHAN) { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; + conn->info_ident = 0; l2cap_conn_start(conn); } @@ -2589,7 +2611,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl goto drop; skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); @@ -2611,7 +2633,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl } skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { -- cgit v1.2.3-70-g09d2 From f29972de8e7476706ab3c01304a505e7c95d9040 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 05:07:45 +0100 Subject: Bluetooth: Add CID field to L2CAP socket address structure In preparation for L2CAP fixed channel support, the CID value of a L2CAP connection needs to be accessible via the socket interface. The CID is the connection identifier and exists as source and destination value. So extend the L2CAP socket address structure with this field and change getsockname() and getpeername() to fill it in. The bind() and connect() functions have been modified to handle L2CAP socket address structures of variable sizes. This makes them future proof if additional fields need to be added. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap.c | 55 ++++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index abfec884771..54737c5dc86 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -37,6 +37,7 @@ struct sockaddr_l2 { sa_family_t l2_family; __le16 l2_psm; bdaddr_t l2_bdaddr; + __le16 l2_cid; }; /* L2CAP socket options */ diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 985366c36f4..7bba469b682 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -770,17 +770,21 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol) return 0; } -static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; - int err = 0; + struct sockaddr_l2 la; + int len, err = 0; - BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); + BT_DBG("sk %p", sk); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + lock_sock(sk); if (sk->sk_state != BT_OPEN) { @@ -788,7 +792,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ goto done; } - if (la->l2_psm && btohs(la->l2_psm) < 0x1001 && + if (la.l2_psm && btohs(la.l2_psm) < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { err = -EACCES; goto done; @@ -796,16 +800,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ write_lock_bh(&l2cap_sk_list.lock); - if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) { + if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bt_sk(sk)->src, &la->l2_bdaddr); - l2cap_pi(sk)->psm = la->l2_psm; - l2cap_pi(sk)->sport = la->l2_psm; + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->sport = la.l2_psm; sk->sk_state = BT_BOUND; - if (btohs(la->l2_psm) == 0x0001 || btohs(la->l2_psm) == 0x0003) + if (btohs(la.l2_psm) == 0x0001 || btohs(la.l2_psm) == 0x0003) l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } @@ -826,7 +830,8 @@ static int l2cap_do_connect(struct sock *sk) __u8 auth_type; int err = 0; - BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); + BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), + l2cap_pi(sk)->psm); if (!(hdev = hci_get_route(dst, src))) return -EHOSTUNREACH; @@ -906,20 +911,24 @@ done: static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; - int err = 0; + struct sockaddr_l2 la; + int len, err = 0; lock_sock(sk); BT_DBG("sk %p", sk); - if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { + if (!addr || addr->sa_family != AF_BLUETOOTH) { err = -EINVAL; goto done; } - if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) { + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) { err = -EINVAL; goto done; } @@ -946,8 +955,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al } /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr); - l2cap_pi(sk)->psm = la->l2_psm; + bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; if ((err = l2cap_do_connect(sk))) goto done; @@ -1071,12 +1080,16 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); - if (peer) + if (peer) { + la->l2_psm = l2cap_pi(sk)->psm; bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - else + la->l2_cid = htobs(l2cap_pi(sk)->dcid); + } else { + la->l2_psm = l2cap_pi(sk)->sport; bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = htobs(l2cap_pi(sk)->scid); + } - la->l2_psm = l2cap_pi(sk)->psm; return 0; } @@ -1208,7 +1221,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us { struct sock *sk = sock->sk; struct l2cap_options opts; - int err = 0, len; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); -- cgit v1.2.3-70-g09d2 From 2950f21acb0f6b8fcd964485c2ebf1e06545ac20 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 12 Feb 2009 14:02:50 +0100 Subject: Bluetooth: Ask upper layers for HCI disconnect reason Some of the qualification tests demand that in case of failures in L2CAP the HCI disconnect should indicate a reason why L2CAP fails. This is a bluntly layer violation since multiple L2CAP connections could be using the same ACL and thus forcing a disconnect reason is not a good idea. To comply with the Bluetooth test specification, the disconnect reason is now stored in the L2CAP connection structure and every time a new L2CAP channel is added it will set back to its default. So only in the case where the L2CAP channel with the disconnect reason is really the last one, it will propagated to the HCI layer. The HCI layer has been extended with a disconnect indication that allows it to ask upper layers for a disconnect reason. The upper layer must not support this callback and in that case it will nicely default to the existing behavior. If an upper layer like L2CAP can provide a disconnect reason that one will be used to disconnect the ACL or SCO link. No modification to the ACL disconnect timeout have been made. So in case of Linux to Linux connection the initiator will disconnect the ACL link before the acceptor side can signal the specific disconnect reason. That is perfectly fine since Linux doesn't make use of this value anyway. The L2CAP layer has a perfect valid error code for rejecting connection due to a security violation. It is unclear why the Bluetooth specification insists on having specific HCI disconnect reason. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 25 +++++++++++++++++++++---- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/hci_conn.c | 6 ++++-- net/bluetooth/hci_event.c | 2 +- net/bluetooth/l2cap.c | 20 +++++++++++++++++++- net/bluetooth/sco.c | 4 ++-- 6 files changed, 49 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9473fce499e..01f9316b4c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -478,7 +478,8 @@ struct hci_proto { int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); int (*connect_cfm) (struct hci_conn *conn, __u8 status); - int (*disconn_ind) (struct hci_conn *conn, __u8 reason); + int (*disconn_ind) (struct hci_conn *conn); + int (*disconn_cfm) (struct hci_conn *conn, __u8 reason); int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); @@ -513,17 +514,33 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) hp->connect_cfm(conn, status); } -static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason) +static inline int hci_proto_disconn_ind(struct hci_conn *conn) { register struct hci_proto *hp; + int reason = 0x13; hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->disconn_ind) - hp->disconn_ind(conn, reason); + reason = hp->disconn_ind(conn); hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->disconn_ind) - hp->disconn_ind(conn, reason); + reason = hp->disconn_ind(conn); + + return reason; +} + +static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) +{ + register struct hci_proto *hp; + + hp = hci_proto[HCI_PROTO_L2CAP]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); + + hp = hci_proto[HCI_PROTO_SCO]; + if (hp && hp->disconn_cfm) + hp->disconn_cfm(conn, reason); } static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 54737c5dc86..f566aa1f0a4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -221,6 +221,8 @@ struct l2cap_conn { __u8 rx_ident; __u8 tx_ident; + __u8 disc_reason; + struct l2cap_chan_list chan_list; }; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index dcdaa4be784..96281a11a18 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -159,6 +159,7 @@ static void hci_conn_timeout(unsigned long arg) { struct hci_conn *conn = (void *) arg; struct hci_dev *hdev = conn->hdev; + __u8 reason; BT_DBG("conn %p state %d", conn, conn->state); @@ -177,7 +178,8 @@ static void hci_conn_timeout(unsigned long arg) break; case BT_CONFIG: case BT_CONNECTED: - hci_acl_disconn(conn, 0x13); + reason = hci_proto_disconn_ind(conn); + hci_acl_disconn(conn, reason); break; default: conn->state = BT_CLOSED; @@ -562,7 +564,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev) hci_conn_del_sysfs(c); - hci_proto_disconn_ind(c, 0x16); + hci_proto_disconn_cfm(c, 0x16); hci_conn_del(c); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 899b8991a46..c396542c2b8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1021,7 +1021,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_conn_del_sysfs(conn); - hci_proto_disconn_ind(conn, ev->reason); + hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7bba469b682..d563f2ebcbb 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -206,6 +206,8 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); + conn->disc_reason = 0x13; + l2cap_pi(sk)->conn = conn; if (sk->sk_type == SOCK_SEQPACKET) { @@ -491,6 +493,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); + conn->disc_reason = 0x13; + return conn; } @@ -1840,6 +1844,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if the ACL is secure enough (if not SDP) */ if (psm != cpu_to_le16(0x0001) && !hci_conn_check_link_mode(conn->hcon)) { + conn->disc_reason = 0x05; result = L2CAP_CR_SEC_BLOCK; goto response; } @@ -2472,7 +2477,19 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) return 0; } -static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason) +static int l2cap_disconn_ind(struct hci_conn *hcon) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + + BT_DBG("hcon %p", hcon); + + if (hcon->type != ACL_LINK || !conn) + return 0x13; + + return conn->disc_reason; +} + +static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); @@ -2717,6 +2734,7 @@ static struct hci_proto l2cap_hci_proto = { .connect_ind = l2cap_connect_ind, .connect_cfm = l2cap_connect_cfm, .disconn_ind = l2cap_disconn_ind, + .disconn_cfm = l2cap_disconn_cfm, .security_cfm = l2cap_security_cfm, .recv_acldata = l2cap_recv_acldata }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 7f10f97cd69..51ae0c3e470 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -902,7 +902,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) return 0; } -static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason) +static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); @@ -985,7 +985,7 @@ static struct hci_proto sco_hci_proto = { .id = HCI_PROTO_SCO, .connect_ind = sco_connect_ind, .connect_cfm = sco_connect_cfm, - .disconn_ind = sco_disconn_ind, + .disconn_cfm = sco_disconn_cfm, .recv_scodata = sco_recv_scodata }; -- cgit v1.2.3-70-g09d2 From 56bca31ff1989aa8b60f717e984b0e624f06324e Mon Sep 17 00:00:00 2001 From: Hannes Eder Date: Wed, 25 Feb 2009 10:32:52 +0000 Subject: inet fragments: fix sparse warning: context imbalance Impact: Attribute function with __releases(...) Fix this sparse warning: net/ipv4/inet_fragment.c:276:35: warning: context imbalance in 'inet_frag_find' - unexpected unlock Signed-off-by: Hannes Eder Signed-off-by: David S. Miller --- include/net/inet_frag.h | 3 ++- net/ipv4/inet_fragment.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index e081eefd6f4..39f2dc94390 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -61,7 +61,8 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, int *work); int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f); struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, - struct inet_frags *f, void *key, unsigned int hash); + struct inet_frags *f, void *key, unsigned int hash) + __releases(&f->lock); static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f) { diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 6c52e08f786..eaf3e2c8646 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -267,6 +267,7 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) + __releases(&f->lock) { struct inet_frag_queue *q; struct hlist_node *n; -- cgit v1.2.3-70-g09d2 From f3734ee6df3ac57151e02d091f47d5e52e646539 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Feb 2009 12:32:55 -0500 Subject: make net/ieee80211.h private to ipw2x00 Only ipw2x00 now uses it. Reduce confusion. Profit! Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/Kconfig | 2 +- drivers/net/wireless/ipw2x00/ieee80211.h | 1185 ++++++++++++++++++++++++++ drivers/net/wireless/ipw2x00/ipw2100.h | 4 +- drivers/net/wireless/ipw2x00/ipw2200.h | 3 +- drivers/net/wireless/ipw2x00/libipw_geo.c | 2 +- drivers/net/wireless/ipw2x00/libipw_module.c | 2 +- drivers/net/wireless/ipw2x00/libipw_rx.c | 3 +- drivers/net/wireless/ipw2x00/libipw_tx.c | 2 +- drivers/net/wireless/ipw2x00/libipw_wx.c | 3 +- include/net/ieee80211.h | 1185 -------------------------- 10 files changed, 1197 insertions(+), 1194 deletions(-) create mode 100644 drivers/net/wireless/ipw2x00/ieee80211.h delete mode 100644 include/net/ieee80211.h (limited to 'include/net') diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig index 1d5dc3e9c5f..85cc79995f6 100644 --- a/drivers/net/wireless/ipw2x00/Kconfig +++ b/drivers/net/wireless/ipw2x00/Kconfig @@ -186,7 +186,7 @@ config LIBIPW_DEBUG % echo 0x00000FFO > /proc/net/ieee80211/debug_level For a list of values you can assign to debug_level, you - can look at the bit mask values in + can look at the bit mask values in ieee80211.h If you are not trying to debug or develop the libipw component, you most likely want to say N here. diff --git a/drivers/net/wireless/ipw2x00/ieee80211.h b/drivers/net/wireless/ipw2x00/ieee80211.h new file mode 100644 index 00000000000..adb7cf31f78 --- /dev/null +++ b/drivers/net/wireless/ipw2x00/ieee80211.h @@ -0,0 +1,1185 @@ +/* + * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are based on Host AP (software wireless + * LAN access point) driver for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * Copyright (c) 2004-2005, Intel Corporation + * + * 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. See README and COPYING for + * more details. + * + * API Version History + * 1.0.x -- Initial version + * 1.1.x -- Added radiotap, QoS, TIM, ieee80211_geo APIs, + * various structure changes, and crypto API init method + */ +#ifndef IEEE80211_H +#define IEEE80211_H +#include /* ETH_ALEN */ +#include /* ARRAY_SIZE */ +#include +#include + +#include + +#define IEEE80211_VERSION "git-1.1.13" + +#define IEEE80211_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + +#define IEEE80211_1ADDR_LEN 10 +#define IEEE80211_2ADDR_LEN 16 +#define IEEE80211_3ADDR_LEN 24 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_FCS_LEN 4 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2346U + +/* Frame control field constants */ +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +/* QOS control */ +#define IEEE80211_QCTL_TID 0x000F + +/* debug macros */ + +#ifdef CONFIG_IEEE80211_DEBUG +extern u32 ieee80211_debug_level; +#define IEEE80211_DEBUG(level, fmt, args...) \ +do { if (ieee80211_debug_level & (level)) \ + printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) +static inline bool ieee80211_ratelimit_debug(u32 level) +{ + return (ieee80211_debug_level & level) && net_ratelimit(); +} +#else +#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) +static inline bool ieee80211_ratelimit_debug(u32 level) +{ + return false; +} +#endif /* CONFIG_IEEE80211_DEBUG */ + +/* + * To use the debug system: + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define IEEE80211_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your + * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ieee80211/debug_level + * + * you simply need to add your entry to the ieee80211_debug_level array. + * + * If you do not see debug_level in /proc/net/ieee80211 then you do not have + * CONFIG_IEEE80211_DEBUG defined in your kernel configuration + * + */ + +#define IEEE80211_DL_INFO (1<<0) +#define IEEE80211_DL_WX (1<<1) +#define IEEE80211_DL_SCAN (1<<2) +#define IEEE80211_DL_STATE (1<<3) +#define IEEE80211_DL_MGMT (1<<4) +#define IEEE80211_DL_FRAG (1<<5) +#define IEEE80211_DL_DROP (1<<7) + +#define IEEE80211_DL_TX (1<<8) +#define IEEE80211_DL_RX (1<<9) +#define IEEE80211_DL_QOS (1<<31) + +#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) +#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) +#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) + +#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) +#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) +#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) +#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) +#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) +#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a) +#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a) +#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a) +#define IEEE80211_DEBUG_QOS(f, a...) IEEE80211_DEBUG(IEEE80211_DL_QOS, f, ## a) +#include +#include /* ARPHRD_ETHER */ + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY /* enable iwspy support */ +#endif +#include /* new driver API */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct ieee80211_snap_hdr { + + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __attribute__ ((packed)); + +#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) + +#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) +#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) + +/* Action categories - 802.11h */ +enum ieee80211_actioncategories { + WLAN_ACTION_SPECTRUM_MGMT = 0, + /* Reserved 1-127 */ + /* Error 128-255 */ +}; + +/* Action details - 802.11h */ +enum ieee80211_actiondetails { + WLAN_ACTION_CATEGORY_MEASURE_REQUEST = 0, + WLAN_ACTION_CATEGORY_MEASURE_REPORT = 1, + WLAN_ACTION_CATEGORY_TPC_REQUEST = 2, + WLAN_ACTION_CATEGORY_TPC_REPORT = 3, + WLAN_ACTION_CATEGORY_CHANNEL_SWITCH = 4, + /* 5 - 255 Reserved */ +}; + +#define IEEE80211_STATMASK_SIGNAL (1<<0) +#define IEEE80211_STATMASK_RSSI (1<<1) +#define IEEE80211_STATMASK_NOISE (1<<2) +#define IEEE80211_STATMASK_RATE (1<<3) +#define IEEE80211_STATMASK_WEMASK 0x7 + +#define IEEE80211_CCK_MODULATION (1<<0) +#define IEEE80211_OFDM_MODULATION (1<<1) + +#define IEEE80211_24GHZ_BAND (1<<0) +#define IEEE80211_52GHZ_BAND (1<<1) + +#define IEEE80211_CCK_RATE_1MB 0x02 +#define IEEE80211_CCK_RATE_2MB 0x04 +#define IEEE80211_CCK_RATE_5MB 0x0B +#define IEEE80211_CCK_RATE_11MB 0x16 +#define IEEE80211_OFDM_RATE_6MB 0x0C +#define IEEE80211_OFDM_RATE_9MB 0x12 +#define IEEE80211_OFDM_RATE_12MB 0x18 +#define IEEE80211_OFDM_RATE_18MB 0x24 +#define IEEE80211_OFDM_RATE_24MB 0x30 +#define IEEE80211_OFDM_RATE_36MB 0x48 +#define IEEE80211_OFDM_RATE_48MB 0x60 +#define IEEE80211_OFDM_RATE_54MB 0x6C +#define IEEE80211_BASIC_RATE_MASK 0x80 + +#define IEEE80211_CCK_RATE_1MB_MASK (1<<0) +#define IEEE80211_CCK_RATE_2MB_MASK (1<<1) +#define IEEE80211_CCK_RATE_5MB_MASK (1<<2) +#define IEEE80211_CCK_RATE_11MB_MASK (1<<3) +#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) +#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) +#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) +#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) +#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) +#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) +#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) +#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) + +#define IEEE80211_CCK_RATES_MASK 0x0000000F +#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ + IEEE80211_CCK_RATE_2MB_MASK) +#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ + IEEE80211_CCK_RATE_5MB_MASK | \ + IEEE80211_CCK_RATE_11MB_MASK) + +#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 +#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ + IEEE80211_OFDM_RATE_12MB_MASK | \ + IEEE80211_OFDM_RATE_24MB_MASK) +#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ + IEEE80211_OFDM_RATE_9MB_MASK | \ + IEEE80211_OFDM_RATE_18MB_MASK | \ + IEEE80211_OFDM_RATE_36MB_MASK | \ + IEEE80211_OFDM_RATE_48MB_MASK | \ + IEEE80211_OFDM_RATE_54MB_MASK) +#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ + IEEE80211_CCK_DEFAULT_RATES_MASK) + +#define IEEE80211_NUM_OFDM_RATES 8 +#define IEEE80211_NUM_CCK_RATES 4 +#define IEEE80211_OFDM_SHIFT_MASK_A 4 + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. + * For ieee80211_rx_mgt, you need to set at least the 'len' parameter. + */ +struct ieee80211_rx_stats { + u32 mac_time; + s8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 received_channel; + u8 control; + u8 mask; + u8 freq; + u16 len; + u64 tsf; + u32 beacon_time; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define IEEE80211_FRAG_CACHE_LEN 4 + +struct ieee80211_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct ieee80211_stats { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + +struct ieee80211_device; + +#define SEC_KEY_1 (1<<0) +#define SEC_KEY_2 (1<<1) +#define SEC_KEY_3 (1<<2) +#define SEC_KEY_4 (1<<3) +#define SEC_ACTIVE_KEY (1<<4) +#define SEC_AUTH_MODE (1<<5) +#define SEC_UNICAST_GROUP (1<<6) +#define SEC_LEVEL (1<<7) +#define SEC_ENABLED (1<<8) +#define SEC_ENCRYPT (1<<9) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define SEC_ALG_NONE 0 +#define SEC_ALG_WEP 1 +#define SEC_ALG_TKIP 2 +#define SEC_ALG_CCMP 3 + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 +#define SCM_KEY_LEN 32 +#define SCM_TEMPORAL_KEY_LENGTH 16 + +struct ieee80211_security { + u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; + u8 auth_mode; + u8 encode_alg[WEP_KEYS]; + u8 key_sizes[WEP_KEYS]; + u8 keys[WEP_KEYS][SCM_KEY_LEN]; + u8 level; + u16 flags; +} __attribute__ ((packed)); + +/* + + 802.11 data frame from AP + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `-------------------------------------------------------------------' + +Total: 28-2340 bytes + +*/ + +#define BEACON_PROBE_SSID_ID_POSITION 12 + +/* Management Frame Information Element Types */ +enum ieee80211_mfie { + MFIE_TYPE_SSID = 0, + MFIE_TYPE_RATES = 1, + MFIE_TYPE_FH_SET = 2, + MFIE_TYPE_DS_SET = 3, + MFIE_TYPE_CF_SET = 4, + MFIE_TYPE_TIM = 5, + MFIE_TYPE_IBSS_SET = 6, + MFIE_TYPE_COUNTRY = 7, + MFIE_TYPE_HOP_PARAMS = 8, + MFIE_TYPE_HOP_TABLE = 9, + MFIE_TYPE_REQUEST = 10, + MFIE_TYPE_CHALLENGE = 16, + MFIE_TYPE_POWER_CONSTRAINT = 32, + MFIE_TYPE_POWER_CAPABILITY = 33, + MFIE_TYPE_TPC_REQUEST = 34, + MFIE_TYPE_TPC_REPORT = 35, + MFIE_TYPE_SUPP_CHANNELS = 36, + MFIE_TYPE_CSA = 37, + MFIE_TYPE_MEASURE_REQUEST = 38, + MFIE_TYPE_MEASURE_REPORT = 39, + MFIE_TYPE_QUIET = 40, + MFIE_TYPE_IBSS_DFS = 41, + MFIE_TYPE_ERP_INFO = 42, + MFIE_TYPE_RSN = 48, + MFIE_TYPE_RATES_EX = 50, + MFIE_TYPE_GENERIC = 221, + MFIE_TYPE_QOS_PARAMETER = 222, +}; + +struct ieee80211_hdr_1addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 payload[0]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_2addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 payload[0]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[0]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_4addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 addr4[ETH_ALEN]; + u8 payload[0]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addrqos { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[0]; + __le16 qos_ctl; +} __attribute__ ((packed)); + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __attribute__ ((packed)); + +/* + * These are the data types that can make up management packets + * + u16 auth_algorithm; + u16 auth_sequence; + u16 beacon_interval; + u16 capability; + u8 current_ap[ETH_ALEN]; + u16 listen_interval; + struct { + u16 association_id:14, reserved:2; + } __attribute__ ((packed)); + u32 time_stamp[2]; + u16 reason; + u16 status; +*/ + +struct ieee80211_auth { + struct ieee80211_hdr_3addr header; + __le16 algorithm; + __le16 transaction; + __le16 status; + /* challenge */ + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +struct ieee80211_channel_switch { + u8 id; + u8 len; + u8 mode; + u8 channel; + u8 count; +} __attribute__ ((packed)); + +struct ieee80211_action { + struct ieee80211_hdr_3addr header; + u8 category; + u8 action; + union { + struct ieee80211_action_exchange { + u8 token; + struct ieee80211_info_element info_element[0]; + } exchange; + struct ieee80211_channel_switch channel_switch; + + } format; +} __attribute__ ((packed)); + +struct ieee80211_disassoc { + struct ieee80211_hdr_3addr header; + __le16 reason; +} __attribute__ ((packed)); + +/* Alias deauth for disassoc */ +#define ieee80211_deauth ieee80211_disassoc + +struct ieee80211_probe_request { + struct ieee80211_hdr_3addr header; + /* SSID, supported rates */ + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +struct ieee80211_probe_response { + struct ieee80211_hdr_3addr header; + __le32 time_stamp[2]; + __le16 beacon_interval; + __le16 capability; + /* SSID, supported rates, FH params, DS params, + * CF params, IBSS params, TIM (if beacon), RSN */ + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +/* Alias beacon for probe_response */ +#define ieee80211_beacon ieee80211_probe_response + +struct ieee80211_assoc_request { + struct ieee80211_hdr_3addr header; + __le16 capability; + __le16 listen_interval; + /* SSID, supported rates, RSN */ + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +struct ieee80211_reassoc_request { + struct ieee80211_hdr_3addr header; + __le16 capability; + __le16 listen_interval; + u8 current_ap[ETH_ALEN]; + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +struct ieee80211_assoc_response { + struct ieee80211_hdr_3addr header; + __le16 capability; + __le16 status; + __le16 aid; + /* supported rates */ + struct ieee80211_info_element info_element[0]; +} __attribute__ ((packed)); + +struct ieee80211_txb { + u8 nr_frags; + u8 encrypted; + u8 rts_included; + u8 reserved; + u16 frag_size; + u16 payload_size; + struct sk_buff *fragments[0]; +}; + +/* SWEEP TABLE ENTRIES NUMBER */ +#define MAX_SWEEP_TAB_ENTRIES 42 +#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 128 + +#define CRC_LENGTH 4U + +#define MAX_WPA_IE_LEN 64 + +#define NETWORK_HAS_OFDM (1<<1) +#define NETWORK_HAS_CCK (1<<2) + +/* QoS structure */ +#define NETWORK_HAS_QOS_PARAMETERS (1<<3) +#define NETWORK_HAS_QOS_INFORMATION (1<<4) +#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ + NETWORK_HAS_QOS_INFORMATION) + +/* 802.11h */ +#define NETWORK_HAS_POWER_CONSTRAINT (1<<5) +#define NETWORK_HAS_CSA (1<<6) +#define NETWORK_HAS_QUIET (1<<7) +#define NETWORK_HAS_IBSS_DFS (1<<8) +#define NETWORK_HAS_TPC_REPORT (1<<9) + +#define NETWORK_HAS_ERP_VALUE (1<<10) + +#define QOS_QUEUE_NUM 4 +#define QOS_OUI_LEN 3 +#define QOS_OUI_TYPE 2 +#define QOS_ELEMENT_ID 221 +#define QOS_OUI_INFO_SUB_TYPE 0 +#define QOS_OUI_PARAM_SUB_TYPE 1 +#define QOS_VERSION_1 1 +#define QOS_AIFSN_MIN_VALUE 2 + +struct ieee80211_qos_information_element { + u8 elementID; + u8 length; + u8 qui[QOS_OUI_LEN]; + u8 qui_type; + u8 qui_subtype; + u8 version; + u8 ac_info; +} __attribute__ ((packed)); + +struct ieee80211_qos_ac_parameter { + u8 aci_aifsn; + u8 ecw_min_max; + __le16 tx_op_limit; +} __attribute__ ((packed)); + +struct ieee80211_qos_parameter_info { + struct ieee80211_qos_information_element info_element; + u8 reserved; + struct ieee80211_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; +} __attribute__ ((packed)); + +struct ieee80211_qos_parameters { + __le16 cw_min[QOS_QUEUE_NUM]; + __le16 cw_max[QOS_QUEUE_NUM]; + u8 aifs[QOS_QUEUE_NUM]; + u8 flag[QOS_QUEUE_NUM]; + __le16 tx_op_limit[QOS_QUEUE_NUM]; +} __attribute__ ((packed)); + +struct ieee80211_qos_data { + struct ieee80211_qos_parameters parameters; + int active; + int supported; + u8 param_count; + u8 old_param_count; +}; + +struct ieee80211_tim_parameters { + u8 tim_count; + u8 tim_period; +} __attribute__ ((packed)); + +/*******************************************************/ + +enum { /* ieee80211_basic_report.map */ + IEEE80211_BASIC_MAP_BSS = (1 << 0), + IEEE80211_BASIC_MAP_OFDM = (1 << 1), + IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), + IEEE80211_BASIC_MAP_RADAR = (1 << 3), + IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), + /* Bits 5-7 are reserved */ + +}; +struct ieee80211_basic_report { + u8 channel; + __le64 start_time; + __le16 duration; + u8 map; +} __attribute__ ((packed)); + +enum { /* ieee80211_measurement_request.mode */ + /* Bit 0 is reserved */ + IEEE80211_MEASUREMENT_ENABLE = (1 << 1), + IEEE80211_MEASUREMENT_REQUEST = (1 << 2), + IEEE80211_MEASUREMENT_REPORT = (1 << 3), + /* Bits 4-7 are reserved */ +}; + +enum { + IEEE80211_REPORT_BASIC = 0, /* required */ + IEEE80211_REPORT_CCA = 1, /* optional */ + IEEE80211_REPORT_RPI = 2, /* optional */ + /* 3-255 reserved */ +}; + +struct ieee80211_measurement_params { + u8 channel; + __le64 start_time; + __le16 duration; +} __attribute__ ((packed)); + +struct ieee80211_measurement_request { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + struct ieee80211_measurement_params params[0]; +} __attribute__ ((packed)); + +struct ieee80211_measurement_report { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + union { + struct ieee80211_basic_report basic[0]; + } u; +} __attribute__ ((packed)); + +struct ieee80211_tpc_report { + u8 transmit_power; + u8 link_margin; +} __attribute__ ((packed)); + +struct ieee80211_channel_map { + u8 channel; + u8 map; +} __attribute__ ((packed)); + +struct ieee80211_ibss_dfs { + struct ieee80211_info_element ie; + u8 owner[ETH_ALEN]; + u8 recovery_interval; + struct ieee80211_channel_map channel_map[0]; +}; + +struct ieee80211_csa { + u8 mode; + u8 channel; + u8 count; +} __attribute__ ((packed)); + +struct ieee80211_quiet { + u8 count; + u8 period; + u8 duration; + u8 offset; +} __attribute__ ((packed)); + +struct ieee80211_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; + u8 channel; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; + + struct ieee80211_qos_data qos_data; + + /* These are network statistics */ + struct ieee80211_rx_stats stats; + u16 capability; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u32 flags; + u32 last_associate; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; + u8 erp_value; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + struct ieee80211_tim_parameters tim; + + /* 802.11h info */ + + /* Power Constraint - mandatory if spctrm mgmt required */ + u8 power_constraint; + + /* TPC Report - mandatory if spctrm mgmt required */ + struct ieee80211_tpc_report tpc_report; + + /* IBSS DFS - mandatory if spctrm mgmt required and IBSS + * NOTE: This is variable length and so must be allocated dynamically */ + struct ieee80211_ibss_dfs *ibss_dfs; + + /* Channel Switch Announcement - optional if spctrm mgmt required */ + struct ieee80211_csa csa; + + /* Quiet - optional if spctrm mgmt required */ + struct ieee80211_quiet quiet; + + struct list_head list; +}; + +enum ieee80211_state { + IEEE80211_UNINITIALIZED = 0, + IEEE80211_INITIALIZED, + IEEE80211_ASSOCIATING, + IEEE80211_ASSOCIATED, + IEEE80211_AUTHENTICATING, + IEEE80211_AUTHENTICATED, + IEEE80211_SHUTDOWN +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2346 + +#define CFG_IEEE80211_RESERVE_FCS (1<<0) +#define CFG_IEEE80211_COMPUTE_FCS (1<<1) +#define CFG_IEEE80211_RTS (1<<2) + +#define IEEE80211_24GHZ_MIN_CHANNEL 1 +#define IEEE80211_24GHZ_MAX_CHANNEL 14 +#define IEEE80211_24GHZ_CHANNELS (IEEE80211_24GHZ_MAX_CHANNEL - \ + IEEE80211_24GHZ_MIN_CHANNEL + 1) + +#define IEEE80211_52GHZ_MIN_CHANNEL 34 +#define IEEE80211_52GHZ_MAX_CHANNEL 165 +#define IEEE80211_52GHZ_CHANNELS (IEEE80211_52GHZ_MAX_CHANNEL - \ + IEEE80211_52GHZ_MIN_CHANNEL + 1) + +enum { + IEEE80211_CH_PASSIVE_ONLY = (1 << 0), + IEEE80211_CH_80211H_RULES = (1 << 1), + IEEE80211_CH_B_ONLY = (1 << 2), + IEEE80211_CH_NO_IBSS = (1 << 3), + IEEE80211_CH_UNIFORM_SPREADING = (1 << 4), + IEEE80211_CH_RADAR_DETECT = (1 << 5), + IEEE80211_CH_INVALID = (1 << 6), +}; + +struct ieee80211_channel { + u32 freq; /* in MHz */ + u8 channel; + u8 flags; + u8 max_power; /* in dBm */ +}; + +struct ieee80211_geo { + u8 name[4]; + u8 bg_channels; + u8 a_channels; + struct ieee80211_channel bg[IEEE80211_24GHZ_CHANNELS]; + struct ieee80211_channel a[IEEE80211_52GHZ_CHANNELS]; +}; + +struct ieee80211_device { + struct net_device *dev; + struct ieee80211_security sec; + + /* Bookkeeping structures */ + struct net_device_stats stats; + struct ieee80211_stats ieee_stats; + + struct ieee80211_geo geo; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct ieee80211_network *networks; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ + struct iw_spy_data spy_data; /* iwspy support */ + + spinlock_t lock; + + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs */ + u32 config; + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + + int reset_on_keychange; /* Set to 1 if the HW needs to be reset on + * WEP key changes */ + + /* If the host performs {en,de}cryption, then set to 1 */ + int host_encrypt; + int host_encrypt_msdu; + int host_decrypt; + /* host performs multicast decryption */ + int host_mc_decrypt; + + /* host should strip IV and ICV from protected frames */ + /* meaningful only when hardware decryption is being used */ + int host_strip_iv_icv; + + int host_open_frag; + int host_build_iv; + int ieee802_1x; /* is IEEE 802.1X used */ + + /* WPA data */ + int wpa_enabled; + int drop_unencrypted; + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; + + struct lib80211_crypt_info crypt_info; + + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + /* Fragmentation structures */ + struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + u16 fts; /* Fragmentation Threshold */ + u16 rts; /* RTS threshold */ + + /* Association info */ + u8 bssid[ETH_ALEN]; + + enum ieee80211_state state; + + int mode; /* A, B, G */ + int modulation; /* CCK, OFDM */ + int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ + int abg_true; /* ABG flag */ + + int perfect_rssi; + int worst_rssi; + + u16 prev_seq_ctl; /* used to drop duplicate frames */ + + /* Callback functions */ + void (*set_security) (struct net_device * dev, + struct ieee80211_security * sec); + int (*hard_start_xmit) (struct ieee80211_txb * txb, + struct net_device * dev, int pri); + int (*reset_port) (struct net_device * dev); + int (*is_queue_full) (struct net_device * dev, int pri); + + int (*handle_management) (struct net_device * dev, + struct ieee80211_network * network, u16 type); + int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); + + /* Typical STA methods */ + int (*handle_auth) (struct net_device * dev, + struct ieee80211_auth * auth); + int (*handle_deauth) (struct net_device * dev, + struct ieee80211_deauth * auth); + int (*handle_action) (struct net_device * dev, + struct ieee80211_action * action, + struct ieee80211_rx_stats * stats); + int (*handle_disassoc) (struct net_device * dev, + struct ieee80211_disassoc * assoc); + int (*handle_beacon) (struct net_device * dev, + struct ieee80211_beacon * beacon, + struct ieee80211_network * network); + int (*handle_probe_response) (struct net_device * dev, + struct ieee80211_probe_response * resp, + struct ieee80211_network * network); + int (*handle_probe_request) (struct net_device * dev, + struct ieee80211_probe_request * req, + struct ieee80211_rx_stats * stats); + int (*handle_assoc_response) (struct net_device * dev, + struct ieee80211_assoc_response * resp, + struct ieee80211_network * network); + + /* Typical AP methods */ + int (*handle_assoc_request) (struct net_device * dev); + int (*handle_reassoc_request) (struct net_device * dev, + struct ieee80211_reassoc_request * req); + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_ieee80211 */ + u8 priv[0]; +}; + +#define IEEE_A (1<<0) +#define IEEE_B (1<<1) +#define IEEE_G (1<<2) +#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) + +static inline void *ieee80211_priv(struct net_device *dev) +{ + return ((struct ieee80211_device *)netdev_priv(dev))->priv; +} + +static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, + int mode) +{ + /* + * It is possible for both access points and our device to support + * combinations of modes, so as long as there is one valid combination + * of ap/device supported modes, then return success + * + */ + if ((mode & IEEE_A) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_52GHZ_BAND)) + return 1; + + if ((mode & IEEE_G) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + if ((mode & IEEE_B) && + (ieee->modulation & IEEE80211_CCK_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + return 0; +} + +static inline int ieee80211_get_hdrlen(u16 fc) +{ + int hdrlen = IEEE80211_3ADDR_LEN; + u16 stype = WLAN_FC_GET_STYPE(fc); + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = IEEE80211_4ADDR_LEN; + if (stype & IEEE80211_STYPE_QOS_DATA) + hdrlen += 2; + break; + case IEEE80211_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = IEEE80211_1ADDR_LEN; + break; + default: + hdrlen = IEEE80211_2ADDR_LEN; + break; + } + break; + } + + return hdrlen; +} + +static inline u8 *ieee80211_get_payload(struct ieee80211_hdr *hdr) +{ + switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control))) { + case IEEE80211_1ADDR_LEN: + return ((struct ieee80211_hdr_1addr *)hdr)->payload; + case IEEE80211_2ADDR_LEN: + return ((struct ieee80211_hdr_2addr *)hdr)->payload; + case IEEE80211_3ADDR_LEN: + return ((struct ieee80211_hdr_3addr *)hdr)->payload; + case IEEE80211_4ADDR_LEN: + return ((struct ieee80211_hdr_4addr *)hdr)->payload; + } + return NULL; +} + +static inline int ieee80211_is_ofdm_rate(u8 rate) +{ + switch (rate & ~IEEE80211_BASIC_RATE_MASK) { + case IEEE80211_OFDM_RATE_6MB: + case IEEE80211_OFDM_RATE_9MB: + case IEEE80211_OFDM_RATE_12MB: + case IEEE80211_OFDM_RATE_18MB: + case IEEE80211_OFDM_RATE_24MB: + case IEEE80211_OFDM_RATE_36MB: + case IEEE80211_OFDM_RATE_48MB: + case IEEE80211_OFDM_RATE_54MB: + return 1; + } + return 0; +} + +static inline int ieee80211_is_cck_rate(u8 rate) +{ + switch (rate & ~IEEE80211_BASIC_RATE_MASK) { + case IEEE80211_CCK_RATE_1MB: + case IEEE80211_CCK_RATE_2MB: + case IEEE80211_CCK_RATE_5MB: + case IEEE80211_CCK_RATE_11MB: + return 1; + } + return 0; +} + +/* ieee80211.c */ +extern void free_ieee80211(struct net_device *dev); +extern struct net_device *alloc_ieee80211(int sizeof_priv); + +extern int ieee80211_set_encryption(struct ieee80211_device *ieee); + +/* ieee80211_tx.c */ +extern int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev); +extern void ieee80211_txb_free(struct ieee80211_txb *); + +/* ieee80211_rx.c */ +extern void ieee80211_rx_any(struct ieee80211_device *ieee, + struct sk_buff *skb, struct ieee80211_rx_stats *stats); +extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats); +/* make sure to set stats->len */ +extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr_4addr *header, + struct ieee80211_rx_stats *stats); +extern void ieee80211_network_reset(struct ieee80211_network *network); + +/* ieee80211_geo.c */ +extern const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device + *ieee); +extern int ieee80211_set_geo(struct ieee80211_device *ieee, + const struct ieee80211_geo *geo); + +extern int ieee80211_is_valid_channel(struct ieee80211_device *ieee, + u8 channel); +extern int ieee80211_channel_to_index(struct ieee80211_device *ieee, + u8 channel); +extern u8 ieee80211_freq_to_channel(struct ieee80211_device *ieee, u32 freq); +extern u8 ieee80211_get_channel_flags(struct ieee80211_device *ieee, + u8 channel); +extern const struct ieee80211_channel *ieee80211_get_channel(struct + ieee80211_device + *ieee, u8 channel); +extern u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, + u8 channel); + +/* ieee80211_wx.c */ +extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +extern int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +static inline void ieee80211_increment_scans(struct ieee80211_device *ieee) +{ + ieee->scans++; +} + +static inline int ieee80211_get_scans(struct ieee80211_device *ieee) +{ + return ieee->scans; +} + +#endif /* IEEE80211_H */ diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h index bbf1ddcafba..46b135d2167 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.h +++ b/drivers/net/wireless/ipw2x00/ipw2100.h @@ -39,8 +39,6 @@ #include #include // new driver API -#include - #ifdef CONFIG_IPW2100_MONITOR #include #endif @@ -48,6 +46,8 @@ #include #include +#include "ieee80211.h" + struct ipw2100_priv; struct ipw2100_tx_packet; struct ipw2100_rx_packet; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h index 277b274d4be..3e66c998dfe 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/ipw2x00/ipw2200.h @@ -49,13 +49,14 @@ #include #include -#include #include #define DRV_NAME "ipw2200" #include +#include "ieee80211.h" + /* Authentication and Association States */ enum connection_manager_assoc_states { CMAS_INIT = 0, diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/ipw2x00/libipw_geo.c index 960ad13f5e9..9dfbb8760f6 100644 --- a/drivers/net/wireless/ipw2x00/libipw_geo.c +++ b/drivers/net/wireless/ipw2x00/libipw_geo.c @@ -41,7 +41,7 @@ #include #include -#include +#include "ieee80211.h" int ieee80211_is_valid_channel(struct ieee80211_device *ieee, u8 channel) { diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index a2f5616d5b0..0f233ab6a95 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -50,7 +50,7 @@ #include #include -#include +#include "ieee80211.h" #define DRV_DESCRIPTION "802.11 data/management/control stack" #define DRV_NAME "ieee80211" diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index 9c67dfae432..4865475e8a8 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -33,7 +33,8 @@ #include #include -#include + +#include "ieee80211.h" static void ieee80211_monitor_rx(struct ieee80211_device *ieee, struct sk_buff *skb, diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c index f78f57e8844..a874e909191 100644 --- a/drivers/net/wireless/ipw2x00/libipw_tx.c +++ b/drivers/net/wireless/ipw2x00/libipw_tx.c @@ -41,7 +41,7 @@ #include #include -#include +#include "ieee80211.h" /* diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c index 31ea3abfc32..dfbadb3b9bd 100644 --- a/drivers/net/wireless/ipw2x00/libipw_wx.c +++ b/drivers/net/wireless/ipw2x00/libipw_wx.c @@ -35,9 +35,10 @@ #include #include -#include #include +#include "ieee80211.h" + static const char *ieee80211_modes[] = { "?", "a", "b", "ab", "g", "ag", "bg", "abg" }; diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h deleted file mode 100644 index adb7cf31f78..00000000000 --- a/include/net/ieee80211.h +++ /dev/null @@ -1,1185 +0,0 @@ -/* - * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 - * remains copyright by the original authors - * - * Portions of the merged code are based on Host AP (software wireless - * LAN access point) driver for Intersil Prism2/2.5/3. - * - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen - * - * Adaption to a generic IEEE 802.11 stack by James Ketrenos - * - * Copyright (c) 2004-2005, Intel Corporation - * - * 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. See README and COPYING for - * more details. - * - * API Version History - * 1.0.x -- Initial version - * 1.1.x -- Added radiotap, QoS, TIM, ieee80211_geo APIs, - * various structure changes, and crypto API init method - */ -#ifndef IEEE80211_H -#define IEEE80211_H -#include /* ETH_ALEN */ -#include /* ARRAY_SIZE */ -#include -#include - -#include - -#define IEEE80211_VERSION "git-1.1.13" - -#define IEEE80211_DATA_LEN 2304 -/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section - 6.2.1.1.2. - - The figure in section 7.1.2 suggests a body size of up to 2312 - bytes is allowed, which is a bit confusing, I suspect this - represents the 2304 bytes of real data, plus a possible 8 bytes of - WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ - -#define IEEE80211_1ADDR_LEN 10 -#define IEEE80211_2ADDR_LEN 16 -#define IEEE80211_3ADDR_LEN 24 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_FCS_LEN 4 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) - -#define MIN_FRAG_THRESHOLD 256U -#define MAX_FRAG_THRESHOLD 2346U - -/* Frame control field constants */ -#define IEEE80211_FCTL_VERS 0x0003 -#define IEEE80211_FCTL_FTYPE 0x000c -#define IEEE80211_FCTL_STYPE 0x00f0 -#define IEEE80211_FCTL_TODS 0x0100 -#define IEEE80211_FCTL_FROMDS 0x0200 -#define IEEE80211_FCTL_MOREFRAGS 0x0400 -#define IEEE80211_FCTL_RETRY 0x0800 -#define IEEE80211_FCTL_PM 0x1000 -#define IEEE80211_FCTL_MOREDATA 0x2000 -#define IEEE80211_FCTL_PROTECTED 0x4000 -#define IEEE80211_FCTL_ORDER 0x8000 - -#define IEEE80211_FTYPE_MGMT 0x0000 -#define IEEE80211_FTYPE_CTL 0x0004 -#define IEEE80211_FTYPE_DATA 0x0008 - -/* management */ -#define IEEE80211_STYPE_ASSOC_REQ 0x0000 -#define IEEE80211_STYPE_ASSOC_RESP 0x0010 -#define IEEE80211_STYPE_REASSOC_REQ 0x0020 -#define IEEE80211_STYPE_REASSOC_RESP 0x0030 -#define IEEE80211_STYPE_PROBE_REQ 0x0040 -#define IEEE80211_STYPE_PROBE_RESP 0x0050 -#define IEEE80211_STYPE_BEACON 0x0080 -#define IEEE80211_STYPE_ATIM 0x0090 -#define IEEE80211_STYPE_DISASSOC 0x00A0 -#define IEEE80211_STYPE_AUTH 0x00B0 -#define IEEE80211_STYPE_DEAUTH 0x00C0 -#define IEEE80211_STYPE_ACTION 0x00D0 - -/* control */ -#define IEEE80211_STYPE_PSPOLL 0x00A0 -#define IEEE80211_STYPE_RTS 0x00B0 -#define IEEE80211_STYPE_CTS 0x00C0 -#define IEEE80211_STYPE_ACK 0x00D0 -#define IEEE80211_STYPE_CFEND 0x00E0 -#define IEEE80211_STYPE_CFENDACK 0x00F0 - -/* data */ -#define IEEE80211_STYPE_DATA 0x0000 -#define IEEE80211_STYPE_DATA_CFACK 0x0010 -#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 -#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 -#define IEEE80211_STYPE_NULLFUNC 0x0040 -#define IEEE80211_STYPE_CFACK 0x0050 -#define IEEE80211_STYPE_CFPOLL 0x0060 -#define IEEE80211_STYPE_CFACKPOLL 0x0070 -#define IEEE80211_STYPE_QOS_DATA 0x0080 - -#define IEEE80211_SCTL_FRAG 0x000F -#define IEEE80211_SCTL_SEQ 0xFFF0 - -/* QOS control */ -#define IEEE80211_QCTL_TID 0x000F - -/* debug macros */ - -#ifdef CONFIG_IEEE80211_DEBUG -extern u32 ieee80211_debug_level; -#define IEEE80211_DEBUG(level, fmt, args...) \ -do { if (ieee80211_debug_level & (level)) \ - printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) -static inline bool ieee80211_ratelimit_debug(u32 level) -{ - return (ieee80211_debug_level & level) && net_ratelimit(); -} -#else -#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) -static inline bool ieee80211_ratelimit_debug(u32 level) -{ - return false; -} -#endif /* CONFIG_IEEE80211_DEBUG */ - -/* - * To use the debug system: - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of: - * - * #define IEEE80211_DL_xxxx VALUE - * - * shifting value to the left one bit from the previous entry. xxxx should be - * the name of the classification (for example, WEP) - * - * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your - * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * To add your debug level to the list of levels seen when you perform - * - * % cat /proc/net/ieee80211/debug_level - * - * you simply need to add your entry to the ieee80211_debug_level array. - * - * If you do not see debug_level in /proc/net/ieee80211 then you do not have - * CONFIG_IEEE80211_DEBUG defined in your kernel configuration - * - */ - -#define IEEE80211_DL_INFO (1<<0) -#define IEEE80211_DL_WX (1<<1) -#define IEEE80211_DL_SCAN (1<<2) -#define IEEE80211_DL_STATE (1<<3) -#define IEEE80211_DL_MGMT (1<<4) -#define IEEE80211_DL_FRAG (1<<5) -#define IEEE80211_DL_DROP (1<<7) - -#define IEEE80211_DL_TX (1<<8) -#define IEEE80211_DL_RX (1<<9) -#define IEEE80211_DL_QOS (1<<31) - -#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) -#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) -#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) - -#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) -#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) -#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) -#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) -#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) -#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a) -#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a) -#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a) -#define IEEE80211_DEBUG_QOS(f, a...) IEEE80211_DEBUG(IEEE80211_DL_QOS, f, ## a) -#include -#include /* ARPHRD_ETHER */ - -#ifndef WIRELESS_SPY -#define WIRELESS_SPY /* enable iwspy support */ -#endif -#include /* new driver API */ - -#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ - -#ifndef ETH_P_80211_RAW -#define ETH_P_80211_RAW (ETH_P_ECONET + 1) -#endif - -/* IEEE 802.11 defines */ - -#define P80211_OUI_LEN 3 - -struct ieee80211_snap_hdr { - - u8 dsap; /* always 0xAA */ - u8 ssap; /* always 0xAA */ - u8 ctrl; /* always 0x03 */ - u8 oui[P80211_OUI_LEN]; /* organizational universal id */ - -} __attribute__ ((packed)); - -#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) - -#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) -#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) -#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) - -#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) -#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) - -/* Action categories - 802.11h */ -enum ieee80211_actioncategories { - WLAN_ACTION_SPECTRUM_MGMT = 0, - /* Reserved 1-127 */ - /* Error 128-255 */ -}; - -/* Action details - 802.11h */ -enum ieee80211_actiondetails { - WLAN_ACTION_CATEGORY_MEASURE_REQUEST = 0, - WLAN_ACTION_CATEGORY_MEASURE_REPORT = 1, - WLAN_ACTION_CATEGORY_TPC_REQUEST = 2, - WLAN_ACTION_CATEGORY_TPC_REPORT = 3, - WLAN_ACTION_CATEGORY_CHANNEL_SWITCH = 4, - /* 5 - 255 Reserved */ -}; - -#define IEEE80211_STATMASK_SIGNAL (1<<0) -#define IEEE80211_STATMASK_RSSI (1<<1) -#define IEEE80211_STATMASK_NOISE (1<<2) -#define IEEE80211_STATMASK_RATE (1<<3) -#define IEEE80211_STATMASK_WEMASK 0x7 - -#define IEEE80211_CCK_MODULATION (1<<0) -#define IEEE80211_OFDM_MODULATION (1<<1) - -#define IEEE80211_24GHZ_BAND (1<<0) -#define IEEE80211_52GHZ_BAND (1<<1) - -#define IEEE80211_CCK_RATE_1MB 0x02 -#define IEEE80211_CCK_RATE_2MB 0x04 -#define IEEE80211_CCK_RATE_5MB 0x0B -#define IEEE80211_CCK_RATE_11MB 0x16 -#define IEEE80211_OFDM_RATE_6MB 0x0C -#define IEEE80211_OFDM_RATE_9MB 0x12 -#define IEEE80211_OFDM_RATE_12MB 0x18 -#define IEEE80211_OFDM_RATE_18MB 0x24 -#define IEEE80211_OFDM_RATE_24MB 0x30 -#define IEEE80211_OFDM_RATE_36MB 0x48 -#define IEEE80211_OFDM_RATE_48MB 0x60 -#define IEEE80211_OFDM_RATE_54MB 0x6C -#define IEEE80211_BASIC_RATE_MASK 0x80 - -#define IEEE80211_CCK_RATE_1MB_MASK (1<<0) -#define IEEE80211_CCK_RATE_2MB_MASK (1<<1) -#define IEEE80211_CCK_RATE_5MB_MASK (1<<2) -#define IEEE80211_CCK_RATE_11MB_MASK (1<<3) -#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) -#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) -#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) -#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) -#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) -#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) -#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) -#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) - -#define IEEE80211_CCK_RATES_MASK 0x0000000F -#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ - IEEE80211_CCK_RATE_2MB_MASK) -#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ - IEEE80211_CCK_RATE_5MB_MASK | \ - IEEE80211_CCK_RATE_11MB_MASK) - -#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 -#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ - IEEE80211_OFDM_RATE_12MB_MASK | \ - IEEE80211_OFDM_RATE_24MB_MASK) -#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ - IEEE80211_OFDM_RATE_9MB_MASK | \ - IEEE80211_OFDM_RATE_18MB_MASK | \ - IEEE80211_OFDM_RATE_36MB_MASK | \ - IEEE80211_OFDM_RATE_48MB_MASK | \ - IEEE80211_OFDM_RATE_54MB_MASK) -#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ - IEEE80211_CCK_DEFAULT_RATES_MASK) - -#define IEEE80211_NUM_OFDM_RATES 8 -#define IEEE80211_NUM_CCK_RATES 4 -#define IEEE80211_OFDM_SHIFT_MASK_A 4 - -/* NOTE: This data is for statistical purposes; not all hardware provides this - * information for frames received. - * For ieee80211_rx_mgt, you need to set at least the 'len' parameter. - */ -struct ieee80211_rx_stats { - u32 mac_time; - s8 rssi; - u8 signal; - u8 noise; - u16 rate; /* in 100 kbps */ - u8 received_channel; - u8 control; - u8 mask; - u8 freq; - u16 len; - u64 tsf; - u32 beacon_time; -}; - -/* IEEE 802.11 requires that STA supports concurrent reception of at least - * three fragmented frames. This define can be increased to support more - * concurrent frames, but it should be noted that each entry can consume about - * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ -#define IEEE80211_FRAG_CACHE_LEN 4 - -struct ieee80211_frag_entry { - unsigned long first_frag_time; - unsigned int seq; - unsigned int last_frag; - struct sk_buff *skb; - u8 src_addr[ETH_ALEN]; - u8 dst_addr[ETH_ALEN]; -}; - -struct ieee80211_stats { - unsigned int tx_unicast_frames; - unsigned int tx_multicast_frames; - unsigned int tx_fragments; - unsigned int tx_unicast_octets; - unsigned int tx_multicast_octets; - unsigned int tx_deferred_transmissions; - unsigned int tx_single_retry_frames; - unsigned int tx_multiple_retry_frames; - unsigned int tx_retry_limit_exceeded; - unsigned int tx_discards; - unsigned int rx_unicast_frames; - unsigned int rx_multicast_frames; - unsigned int rx_fragments; - unsigned int rx_unicast_octets; - unsigned int rx_multicast_octets; - unsigned int rx_fcs_errors; - unsigned int rx_discards_no_buffer; - unsigned int tx_discards_wrong_sa; - unsigned int rx_discards_undecryptable; - unsigned int rx_message_in_msg_fragments; - unsigned int rx_message_in_bad_msg_fragments; -}; - -struct ieee80211_device; - -#define SEC_KEY_1 (1<<0) -#define SEC_KEY_2 (1<<1) -#define SEC_KEY_3 (1<<2) -#define SEC_KEY_4 (1<<3) -#define SEC_ACTIVE_KEY (1<<4) -#define SEC_AUTH_MODE (1<<5) -#define SEC_UNICAST_GROUP (1<<6) -#define SEC_LEVEL (1<<7) -#define SEC_ENABLED (1<<8) -#define SEC_ENCRYPT (1<<9) - -#define SEC_LEVEL_0 0 /* None */ -#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ -#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ -#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ -#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ - -#define SEC_ALG_NONE 0 -#define SEC_ALG_WEP 1 -#define SEC_ALG_TKIP 2 -#define SEC_ALG_CCMP 3 - -#define WEP_KEYS 4 -#define WEP_KEY_LEN 13 -#define SCM_KEY_LEN 32 -#define SCM_TEMPORAL_KEY_LENGTH 16 - -struct ieee80211_security { - u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; - u8 auth_mode; - u8 encode_alg[WEP_KEYS]; - u8 key_sizes[WEP_KEYS]; - u8 keys[WEP_KEYS][SCM_KEY_LEN]; - u8 level; - u16 flags; -} __attribute__ ((packed)); - -/* - - 802.11 data frame from AP - - ,-------------------------------------------------------------------. -Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | - |------|------|---------|---------|---------|------|---------|------| -Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | - | | tion | (BSSID) | | | ence | data | | - `-------------------------------------------------------------------' - -Total: 28-2340 bytes - -*/ - -#define BEACON_PROBE_SSID_ID_POSITION 12 - -/* Management Frame Information Element Types */ -enum ieee80211_mfie { - MFIE_TYPE_SSID = 0, - MFIE_TYPE_RATES = 1, - MFIE_TYPE_FH_SET = 2, - MFIE_TYPE_DS_SET = 3, - MFIE_TYPE_CF_SET = 4, - MFIE_TYPE_TIM = 5, - MFIE_TYPE_IBSS_SET = 6, - MFIE_TYPE_COUNTRY = 7, - MFIE_TYPE_HOP_PARAMS = 8, - MFIE_TYPE_HOP_TABLE = 9, - MFIE_TYPE_REQUEST = 10, - MFIE_TYPE_CHALLENGE = 16, - MFIE_TYPE_POWER_CONSTRAINT = 32, - MFIE_TYPE_POWER_CAPABILITY = 33, - MFIE_TYPE_TPC_REQUEST = 34, - MFIE_TYPE_TPC_REPORT = 35, - MFIE_TYPE_SUPP_CHANNELS = 36, - MFIE_TYPE_CSA = 37, - MFIE_TYPE_MEASURE_REQUEST = 38, - MFIE_TYPE_MEASURE_REPORT = 39, - MFIE_TYPE_QUIET = 40, - MFIE_TYPE_IBSS_DFS = 41, - MFIE_TYPE_ERP_INFO = 42, - MFIE_TYPE_RSN = 48, - MFIE_TYPE_RATES_EX = 50, - MFIE_TYPE_GENERIC = 221, - MFIE_TYPE_QOS_PARAMETER = 222, -}; - -struct ieee80211_hdr_1addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 payload[0]; -} __attribute__ ((packed)); - -struct ieee80211_hdr_2addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 payload[0]; -} __attribute__ ((packed)); - -struct ieee80211_hdr_3addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 payload[0]; -} __attribute__ ((packed)); - -struct ieee80211_hdr_4addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 addr4[ETH_ALEN]; - u8 payload[0]; -} __attribute__ ((packed)); - -struct ieee80211_hdr_3addrqos { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 payload[0]; - __le16 qos_ctl; -} __attribute__ ((packed)); - -struct ieee80211_info_element { - u8 id; - u8 len; - u8 data[0]; -} __attribute__ ((packed)); - -/* - * These are the data types that can make up management packets - * - u16 auth_algorithm; - u16 auth_sequence; - u16 beacon_interval; - u16 capability; - u8 current_ap[ETH_ALEN]; - u16 listen_interval; - struct { - u16 association_id:14, reserved:2; - } __attribute__ ((packed)); - u32 time_stamp[2]; - u16 reason; - u16 status; -*/ - -struct ieee80211_auth { - struct ieee80211_hdr_3addr header; - __le16 algorithm; - __le16 transaction; - __le16 status; - /* challenge */ - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -struct ieee80211_channel_switch { - u8 id; - u8 len; - u8 mode; - u8 channel; - u8 count; -} __attribute__ ((packed)); - -struct ieee80211_action { - struct ieee80211_hdr_3addr header; - u8 category; - u8 action; - union { - struct ieee80211_action_exchange { - u8 token; - struct ieee80211_info_element info_element[0]; - } exchange; - struct ieee80211_channel_switch channel_switch; - - } format; -} __attribute__ ((packed)); - -struct ieee80211_disassoc { - struct ieee80211_hdr_3addr header; - __le16 reason; -} __attribute__ ((packed)); - -/* Alias deauth for disassoc */ -#define ieee80211_deauth ieee80211_disassoc - -struct ieee80211_probe_request { - struct ieee80211_hdr_3addr header; - /* SSID, supported rates */ - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -struct ieee80211_probe_response { - struct ieee80211_hdr_3addr header; - __le32 time_stamp[2]; - __le16 beacon_interval; - __le16 capability; - /* SSID, supported rates, FH params, DS params, - * CF params, IBSS params, TIM (if beacon), RSN */ - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -/* Alias beacon for probe_response */ -#define ieee80211_beacon ieee80211_probe_response - -struct ieee80211_assoc_request { - struct ieee80211_hdr_3addr header; - __le16 capability; - __le16 listen_interval; - /* SSID, supported rates, RSN */ - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -struct ieee80211_reassoc_request { - struct ieee80211_hdr_3addr header; - __le16 capability; - __le16 listen_interval; - u8 current_ap[ETH_ALEN]; - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -struct ieee80211_assoc_response { - struct ieee80211_hdr_3addr header; - __le16 capability; - __le16 status; - __le16 aid; - /* supported rates */ - struct ieee80211_info_element info_element[0]; -} __attribute__ ((packed)); - -struct ieee80211_txb { - u8 nr_frags; - u8 encrypted; - u8 rts_included; - u8 reserved; - u16 frag_size; - u16 payload_size; - struct sk_buff *fragments[0]; -}; - -/* SWEEP TABLE ENTRIES NUMBER */ -#define MAX_SWEEP_TAB_ENTRIES 42 -#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 -/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs - * only use 8, and then use extended rates for the remaining supported - * rates. Other APs, however, stick all of their supported rates on the - * main rates information element... */ -#define MAX_RATES_LENGTH ((u8)12) -#define MAX_RATES_EX_LENGTH ((u8)16) -#define MAX_NETWORK_COUNT 128 - -#define CRC_LENGTH 4U - -#define MAX_WPA_IE_LEN 64 - -#define NETWORK_HAS_OFDM (1<<1) -#define NETWORK_HAS_CCK (1<<2) - -/* QoS structure */ -#define NETWORK_HAS_QOS_PARAMETERS (1<<3) -#define NETWORK_HAS_QOS_INFORMATION (1<<4) -#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ - NETWORK_HAS_QOS_INFORMATION) - -/* 802.11h */ -#define NETWORK_HAS_POWER_CONSTRAINT (1<<5) -#define NETWORK_HAS_CSA (1<<6) -#define NETWORK_HAS_QUIET (1<<7) -#define NETWORK_HAS_IBSS_DFS (1<<8) -#define NETWORK_HAS_TPC_REPORT (1<<9) - -#define NETWORK_HAS_ERP_VALUE (1<<10) - -#define QOS_QUEUE_NUM 4 -#define QOS_OUI_LEN 3 -#define QOS_OUI_TYPE 2 -#define QOS_ELEMENT_ID 221 -#define QOS_OUI_INFO_SUB_TYPE 0 -#define QOS_OUI_PARAM_SUB_TYPE 1 -#define QOS_VERSION_1 1 -#define QOS_AIFSN_MIN_VALUE 2 - -struct ieee80211_qos_information_element { - u8 elementID; - u8 length; - u8 qui[QOS_OUI_LEN]; - u8 qui_type; - u8 qui_subtype; - u8 version; - u8 ac_info; -} __attribute__ ((packed)); - -struct ieee80211_qos_ac_parameter { - u8 aci_aifsn; - u8 ecw_min_max; - __le16 tx_op_limit; -} __attribute__ ((packed)); - -struct ieee80211_qos_parameter_info { - struct ieee80211_qos_information_element info_element; - u8 reserved; - struct ieee80211_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; -} __attribute__ ((packed)); - -struct ieee80211_qos_parameters { - __le16 cw_min[QOS_QUEUE_NUM]; - __le16 cw_max[QOS_QUEUE_NUM]; - u8 aifs[QOS_QUEUE_NUM]; - u8 flag[QOS_QUEUE_NUM]; - __le16 tx_op_limit[QOS_QUEUE_NUM]; -} __attribute__ ((packed)); - -struct ieee80211_qos_data { - struct ieee80211_qos_parameters parameters; - int active; - int supported; - u8 param_count; - u8 old_param_count; -}; - -struct ieee80211_tim_parameters { - u8 tim_count; - u8 tim_period; -} __attribute__ ((packed)); - -/*******************************************************/ - -enum { /* ieee80211_basic_report.map */ - IEEE80211_BASIC_MAP_BSS = (1 << 0), - IEEE80211_BASIC_MAP_OFDM = (1 << 1), - IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), - IEEE80211_BASIC_MAP_RADAR = (1 << 3), - IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), - /* Bits 5-7 are reserved */ - -}; -struct ieee80211_basic_report { - u8 channel; - __le64 start_time; - __le16 duration; - u8 map; -} __attribute__ ((packed)); - -enum { /* ieee80211_measurement_request.mode */ - /* Bit 0 is reserved */ - IEEE80211_MEASUREMENT_ENABLE = (1 << 1), - IEEE80211_MEASUREMENT_REQUEST = (1 << 2), - IEEE80211_MEASUREMENT_REPORT = (1 << 3), - /* Bits 4-7 are reserved */ -}; - -enum { - IEEE80211_REPORT_BASIC = 0, /* required */ - IEEE80211_REPORT_CCA = 1, /* optional */ - IEEE80211_REPORT_RPI = 2, /* optional */ - /* 3-255 reserved */ -}; - -struct ieee80211_measurement_params { - u8 channel; - __le64 start_time; - __le16 duration; -} __attribute__ ((packed)); - -struct ieee80211_measurement_request { - struct ieee80211_info_element ie; - u8 token; - u8 mode; - u8 type; - struct ieee80211_measurement_params params[0]; -} __attribute__ ((packed)); - -struct ieee80211_measurement_report { - struct ieee80211_info_element ie; - u8 token; - u8 mode; - u8 type; - union { - struct ieee80211_basic_report basic[0]; - } u; -} __attribute__ ((packed)); - -struct ieee80211_tpc_report { - u8 transmit_power; - u8 link_margin; -} __attribute__ ((packed)); - -struct ieee80211_channel_map { - u8 channel; - u8 map; -} __attribute__ ((packed)); - -struct ieee80211_ibss_dfs { - struct ieee80211_info_element ie; - u8 owner[ETH_ALEN]; - u8 recovery_interval; - struct ieee80211_channel_map channel_map[0]; -}; - -struct ieee80211_csa { - u8 mode; - u8 channel; - u8 count; -} __attribute__ ((packed)); - -struct ieee80211_quiet { - u8 count; - u8 period; - u8 duration; - u8 offset; -} __attribute__ ((packed)); - -struct ieee80211_network { - /* These entries are used to identify a unique network */ - u8 bssid[ETH_ALEN]; - u8 channel; - /* Ensure null-terminated for any debug msgs */ - u8 ssid[IW_ESSID_MAX_SIZE + 1]; - u8 ssid_len; - - struct ieee80211_qos_data qos_data; - - /* These are network statistics */ - struct ieee80211_rx_stats stats; - u16 capability; - u8 rates[MAX_RATES_LENGTH]; - u8 rates_len; - u8 rates_ex[MAX_RATES_EX_LENGTH]; - u8 rates_ex_len; - unsigned long last_scanned; - u8 mode; - u32 flags; - u32 last_associate; - u32 time_stamp[2]; - u16 beacon_interval; - u16 listen_interval; - u16 atim_window; - u8 erp_value; - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - struct ieee80211_tim_parameters tim; - - /* 802.11h info */ - - /* Power Constraint - mandatory if spctrm mgmt required */ - u8 power_constraint; - - /* TPC Report - mandatory if spctrm mgmt required */ - struct ieee80211_tpc_report tpc_report; - - /* IBSS DFS - mandatory if spctrm mgmt required and IBSS - * NOTE: This is variable length and so must be allocated dynamically */ - struct ieee80211_ibss_dfs *ibss_dfs; - - /* Channel Switch Announcement - optional if spctrm mgmt required */ - struct ieee80211_csa csa; - - /* Quiet - optional if spctrm mgmt required */ - struct ieee80211_quiet quiet; - - struct list_head list; -}; - -enum ieee80211_state { - IEEE80211_UNINITIALIZED = 0, - IEEE80211_INITIALIZED, - IEEE80211_ASSOCIATING, - IEEE80211_ASSOCIATED, - IEEE80211_AUTHENTICATING, - IEEE80211_AUTHENTICATED, - IEEE80211_SHUTDOWN -}; - -#define DEFAULT_MAX_SCAN_AGE (15 * HZ) -#define DEFAULT_FTS 2346 - -#define CFG_IEEE80211_RESERVE_FCS (1<<0) -#define CFG_IEEE80211_COMPUTE_FCS (1<<1) -#define CFG_IEEE80211_RTS (1<<2) - -#define IEEE80211_24GHZ_MIN_CHANNEL 1 -#define IEEE80211_24GHZ_MAX_CHANNEL 14 -#define IEEE80211_24GHZ_CHANNELS (IEEE80211_24GHZ_MAX_CHANNEL - \ - IEEE80211_24GHZ_MIN_CHANNEL + 1) - -#define IEEE80211_52GHZ_MIN_CHANNEL 34 -#define IEEE80211_52GHZ_MAX_CHANNEL 165 -#define IEEE80211_52GHZ_CHANNELS (IEEE80211_52GHZ_MAX_CHANNEL - \ - IEEE80211_52GHZ_MIN_CHANNEL + 1) - -enum { - IEEE80211_CH_PASSIVE_ONLY = (1 << 0), - IEEE80211_CH_80211H_RULES = (1 << 1), - IEEE80211_CH_B_ONLY = (1 << 2), - IEEE80211_CH_NO_IBSS = (1 << 3), - IEEE80211_CH_UNIFORM_SPREADING = (1 << 4), - IEEE80211_CH_RADAR_DETECT = (1 << 5), - IEEE80211_CH_INVALID = (1 << 6), -}; - -struct ieee80211_channel { - u32 freq; /* in MHz */ - u8 channel; - u8 flags; - u8 max_power; /* in dBm */ -}; - -struct ieee80211_geo { - u8 name[4]; - u8 bg_channels; - u8 a_channels; - struct ieee80211_channel bg[IEEE80211_24GHZ_CHANNELS]; - struct ieee80211_channel a[IEEE80211_52GHZ_CHANNELS]; -}; - -struct ieee80211_device { - struct net_device *dev; - struct ieee80211_security sec; - - /* Bookkeeping structures */ - struct net_device_stats stats; - struct ieee80211_stats ieee_stats; - - struct ieee80211_geo geo; - - /* Probe / Beacon management */ - struct list_head network_free_list; - struct list_head network_list; - struct ieee80211_network *networks; - int scans; - int scan_age; - - int iw_mode; /* operating mode (IW_MODE_*) */ - struct iw_spy_data spy_data; /* iwspy support */ - - spinlock_t lock; - - int tx_headroom; /* Set to size of any additional room needed at front - * of allocated Tx SKBs */ - u32 config; - - /* WEP and other encryption related settings at the device level */ - int open_wep; /* Set to 1 to allow unencrypted frames */ - - int reset_on_keychange; /* Set to 1 if the HW needs to be reset on - * WEP key changes */ - - /* If the host performs {en,de}cryption, then set to 1 */ - int host_encrypt; - int host_encrypt_msdu; - int host_decrypt; - /* host performs multicast decryption */ - int host_mc_decrypt; - - /* host should strip IV and ICV from protected frames */ - /* meaningful only when hardware decryption is being used */ - int host_strip_iv_icv; - - int host_open_frag; - int host_build_iv; - int ieee802_1x; /* is IEEE 802.1X used */ - - /* WPA data */ - int wpa_enabled; - int drop_unencrypted; - int privacy_invoked; - size_t wpa_ie_len; - u8 *wpa_ie; - - struct lib80211_crypt_info crypt_info; - - int bcrx_sta_key; /* use individual keys to override default keys even - * with RX of broad/multicast frames */ - - /* Fragmentation structures */ - struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN]; - unsigned int frag_next_idx; - u16 fts; /* Fragmentation Threshold */ - u16 rts; /* RTS threshold */ - - /* Association info */ - u8 bssid[ETH_ALEN]; - - enum ieee80211_state state; - - int mode; /* A, B, G */ - int modulation; /* CCK, OFDM */ - int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ - int abg_true; /* ABG flag */ - - int perfect_rssi; - int worst_rssi; - - u16 prev_seq_ctl; /* used to drop duplicate frames */ - - /* Callback functions */ - void (*set_security) (struct net_device * dev, - struct ieee80211_security * sec); - int (*hard_start_xmit) (struct ieee80211_txb * txb, - struct net_device * dev, int pri); - int (*reset_port) (struct net_device * dev); - int (*is_queue_full) (struct net_device * dev, int pri); - - int (*handle_management) (struct net_device * dev, - struct ieee80211_network * network, u16 type); - int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); - - /* Typical STA methods */ - int (*handle_auth) (struct net_device * dev, - struct ieee80211_auth * auth); - int (*handle_deauth) (struct net_device * dev, - struct ieee80211_deauth * auth); - int (*handle_action) (struct net_device * dev, - struct ieee80211_action * action, - struct ieee80211_rx_stats * stats); - int (*handle_disassoc) (struct net_device * dev, - struct ieee80211_disassoc * assoc); - int (*handle_beacon) (struct net_device * dev, - struct ieee80211_beacon * beacon, - struct ieee80211_network * network); - int (*handle_probe_response) (struct net_device * dev, - struct ieee80211_probe_response * resp, - struct ieee80211_network * network); - int (*handle_probe_request) (struct net_device * dev, - struct ieee80211_probe_request * req, - struct ieee80211_rx_stats * stats); - int (*handle_assoc_response) (struct net_device * dev, - struct ieee80211_assoc_response * resp, - struct ieee80211_network * network); - - /* Typical AP methods */ - int (*handle_assoc_request) (struct net_device * dev); - int (*handle_reassoc_request) (struct net_device * dev, - struct ieee80211_reassoc_request * req); - - /* This must be the last item so that it points to the data - * allocated beyond this structure by alloc_ieee80211 */ - u8 priv[0]; -}; - -#define IEEE_A (1<<0) -#define IEEE_B (1<<1) -#define IEEE_G (1<<2) -#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) - -static inline void *ieee80211_priv(struct net_device *dev) -{ - return ((struct ieee80211_device *)netdev_priv(dev))->priv; -} - -static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, - int mode) -{ - /* - * It is possible for both access points and our device to support - * combinations of modes, so as long as there is one valid combination - * of ap/device supported modes, then return success - * - */ - if ((mode & IEEE_A) && - (ieee->modulation & IEEE80211_OFDM_MODULATION) && - (ieee->freq_band & IEEE80211_52GHZ_BAND)) - return 1; - - if ((mode & IEEE_G) && - (ieee->modulation & IEEE80211_OFDM_MODULATION) && - (ieee->freq_band & IEEE80211_24GHZ_BAND)) - return 1; - - if ((mode & IEEE_B) && - (ieee->modulation & IEEE80211_CCK_MODULATION) && - (ieee->freq_band & IEEE80211_24GHZ_BAND)) - return 1; - - return 0; -} - -static inline int ieee80211_get_hdrlen(u16 fc) -{ - int hdrlen = IEEE80211_3ADDR_LEN; - u16 stype = WLAN_FC_GET_STYPE(fc); - - switch (WLAN_FC_GET_TYPE(fc)) { - case IEEE80211_FTYPE_DATA: - if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) - hdrlen = IEEE80211_4ADDR_LEN; - if (stype & IEEE80211_STYPE_QOS_DATA) - hdrlen += 2; - break; - case IEEE80211_FTYPE_CTL: - switch (WLAN_FC_GET_STYPE(fc)) { - case IEEE80211_STYPE_CTS: - case IEEE80211_STYPE_ACK: - hdrlen = IEEE80211_1ADDR_LEN; - break; - default: - hdrlen = IEEE80211_2ADDR_LEN; - break; - } - break; - } - - return hdrlen; -} - -static inline u8 *ieee80211_get_payload(struct ieee80211_hdr *hdr) -{ - switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control))) { - case IEEE80211_1ADDR_LEN: - return ((struct ieee80211_hdr_1addr *)hdr)->payload; - case IEEE80211_2ADDR_LEN: - return ((struct ieee80211_hdr_2addr *)hdr)->payload; - case IEEE80211_3ADDR_LEN: - return ((struct ieee80211_hdr_3addr *)hdr)->payload; - case IEEE80211_4ADDR_LEN: - return ((struct ieee80211_hdr_4addr *)hdr)->payload; - } - return NULL; -} - -static inline int ieee80211_is_ofdm_rate(u8 rate) -{ - switch (rate & ~IEEE80211_BASIC_RATE_MASK) { - case IEEE80211_OFDM_RATE_6MB: - case IEEE80211_OFDM_RATE_9MB: - case IEEE80211_OFDM_RATE_12MB: - case IEEE80211_OFDM_RATE_18MB: - case IEEE80211_OFDM_RATE_24MB: - case IEEE80211_OFDM_RATE_36MB: - case IEEE80211_OFDM_RATE_48MB: - case IEEE80211_OFDM_RATE_54MB: - return 1; - } - return 0; -} - -static inline int ieee80211_is_cck_rate(u8 rate) -{ - switch (rate & ~IEEE80211_BASIC_RATE_MASK) { - case IEEE80211_CCK_RATE_1MB: - case IEEE80211_CCK_RATE_2MB: - case IEEE80211_CCK_RATE_5MB: - case IEEE80211_CCK_RATE_11MB: - return 1; - } - return 0; -} - -/* ieee80211.c */ -extern void free_ieee80211(struct net_device *dev); -extern struct net_device *alloc_ieee80211(int sizeof_priv); - -extern int ieee80211_set_encryption(struct ieee80211_device *ieee); - -/* ieee80211_tx.c */ -extern int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev); -extern void ieee80211_txb_free(struct ieee80211_txb *); - -/* ieee80211_rx.c */ -extern void ieee80211_rx_any(struct ieee80211_device *ieee, - struct sk_buff *skb, struct ieee80211_rx_stats *stats); -extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, - struct ieee80211_rx_stats *rx_stats); -/* make sure to set stats->len */ -extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, - struct ieee80211_hdr_4addr *header, - struct ieee80211_rx_stats *stats); -extern void ieee80211_network_reset(struct ieee80211_network *network); - -/* ieee80211_geo.c */ -extern const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device - *ieee); -extern int ieee80211_set_geo(struct ieee80211_device *ieee, - const struct ieee80211_geo *geo); - -extern int ieee80211_is_valid_channel(struct ieee80211_device *ieee, - u8 channel); -extern int ieee80211_channel_to_index(struct ieee80211_device *ieee, - u8 channel); -extern u8 ieee80211_freq_to_channel(struct ieee80211_device *ieee, u32 freq); -extern u8 ieee80211_get_channel_flags(struct ieee80211_device *ieee, - u8 channel); -extern const struct ieee80211_channel *ieee80211_get_channel(struct - ieee80211_device - *ieee, u8 channel); -extern u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, - u8 channel); - -/* ieee80211_wx.c */ -extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); -extern int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); - -static inline void ieee80211_increment_scans(struct ieee80211_device *ieee) -{ - ieee->scans++; -} - -static inline int ieee80211_get_scans(struct ieee80211_device *ieee) -{ - return ieee->scans; -} - -#endif /* IEEE80211_H */ -- cgit v1.2.3-70-g09d2 From 96f5e66e8a79810e2982cdcfa28e554f3d97da21 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Feb 2009 00:51:53 +0100 Subject: mac80211: fix aggregation for hardware with ampdu queues Hardware with AMPDU queues currently has broken aggregation. This patch fixes it by making all A-MPDUs go over the regular AC queues, but keeping track of the hardware queues in mac80211. As a first rough version, it actually stops the AC queue for extended periods of time, which can be removed by adding buffering internal to mac80211, but is currently not a huge problem because people rarely use multiple TIDs that are in the same AC (and iwlwifi currently doesn't operate as AP). This is a short-term fix, my current medium-term plan, which I hope to execute soon as well, but am not sure can finish before .30, looks like this: 1) rework the internal queuing layer in mac80211 that we use for fragments if the driver stopped queue in the middle of a fragmented frame to be able to queue more frames at once (rather than just a single frame with its fragments) 2) instead of stopping the entire AC queue, queue up the frames in a per-station/per-TID queue during aggregation session initiation, when the session has come up take all those frames and put them onto the queue from 1) 3) push the ampdu queue layer abstraction this patch introduces in mac80211 into the driver, and remove the virtual queue stuff from mac80211 again This plan will probably also affect ath9k in that mac80211 queues the frames instead of passing them down, even when there are no ampdu queues. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 -- net/mac80211/agg-tx.c | 186 ++++++++++++++++++++++++++++++--------------- net/mac80211/ieee80211_i.h | 20 +++-- net/mac80211/main.c | 9 ++- net/mac80211/sta_info.c | 15 +++- net/mac80211/sta_info.h | 4 +- net/mac80211/tx.c | 18 +++-- net/mac80211/util.c | 75 ++++++++++++++---- net/mac80211/wme.c | 161 +-------------------------------------- net/mac80211/wme.h | 6 -- 10 files changed, 241 insertions(+), 258 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 88fa3e03e3e..31fd8bab217 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1022,11 +1022,6 @@ static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw) return hw->queues; } -static inline int ieee80211_num_queues(struct ieee80211_hw *hw) -{ - return hw->queues + hw->ampdu_queues; -} - static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1232d9f01ca..0217b68c47c 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -132,9 +132,24 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; - if (local->hw.ampdu_queues) - ieee80211_stop_queue(&local->hw, sta->tid_to_tx_q[tid]); + if (local->hw.ampdu_queues) { + if (initiator) { + /* + * Stop the AC queue to avoid issues where we send + * unaggregated frames already before the delba. + */ + ieee80211_stop_queue_by_reason(&local->hw, + local->hw.queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + /* + * Pretend the driver woke the queue, just in case + * it disabled it before the session was stopped. + */ + ieee80211_wake_queue( + &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); + } *state = HT_AGG_STATE_REQ_STOP_BA_MSK | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); @@ -144,8 +159,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { *state = HT_AGG_STATE_OPERATIONAL; - if (local->hw.ampdu_queues) - ieee80211_wake_queue(&local->hw, sta->tid_to_tx_q[tid]); } return ret; @@ -189,14 +202,19 @@ static void sta_addba_resp_timer_expired(unsigned long data) spin_unlock_bh(&sta->lock); } +static inline int ieee80211_ac_from_tid(int tid) +{ + return ieee802_1d_to_ac[tid & 7]; +} + int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) { struct ieee80211_local *local = hw_to_local(hw); struct sta_info *sta; struct ieee80211_sub_if_data *sdata; - u16 start_seq_num; u8 *state; - int ret = 0; + int i, qn = -1, ret = 0; + u16 start_seq_num; if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; @@ -209,6 +227,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ + if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "rejecting on voice AC\n"); +#endif + return -EINVAL; + } + rcu_read_lock(); sta = sta_info_get(local, ra); @@ -217,7 +242,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) printk(KERN_DEBUG "Could not find the station\n"); #endif ret = -ENOENT; - goto exit; + goto unlock; } /* @@ -230,11 +255,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sta->sdata->vif.type != NL80211_IFTYPE_AP) { ret = -EINVAL; - goto exit; + goto unlock; } spin_lock_bh(&sta->lock); + sdata = sta->sdata; + /* we have tried too many times, receiver does not want A-MPDU */ if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { ret = -EBUSY; @@ -252,6 +279,42 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_unlock_sta; } + if (hw->ampdu_queues) { + spin_lock(&local->queue_stop_reason_lock); + /* reserve a new queue for this session */ + for (i = 0; i < local->hw.ampdu_queues; i++) { + if (local->ampdu_ac_queue[i] < 0) { + qn = i; + local->ampdu_ac_queue[qn] = + ieee80211_ac_from_tid(tid); + break; + } + } + spin_unlock(&local->queue_stop_reason_lock); + + if (qn < 0) { +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "BA request denied - " + "queue unavailable for tid %d\n", tid); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + ret = -ENOSPC; + goto err_unlock_sta; + } + + /* + * If we successfully allocate the session, we can't have + * anything going on on the queue this TID maps into, so + * stop it for now. This is a "virtual" stop using the same + * mechanism that drivers will use. + * + * XXX: queue up frames for this session in the sta_info + * struct instead to avoid hitting all other STAs. + */ + ieee80211_stop_queue_by_reason( + &local->hw, hw->queues + qn, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + /* prepare A-MPDU MLME for Tx aggregation */ sta->ampdu_mlme.tid_tx[tid] = kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); @@ -262,8 +325,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) tid); #endif ret = -ENOMEM; - goto err_unlock_sta; + goto err_return_queue; } + /* Tx timer */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = sta_addba_resp_timer_expired; @@ -271,49 +335,25 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) (unsigned long)&sta->timer_to_tid[tid]; init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); - if (hw->ampdu_queues) { - /* create a new queue for this aggregation */ - ret = ieee80211_ht_agg_queue_add(local, sta, tid); - - /* case no queue is available to aggregation - * don't switch to aggregation */ - if (ret) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "BA request denied - " - "queue unavailable for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - goto err_unlock_queue; - } - } - sdata = sta->sdata; - /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the * call back right away, it must see that the flow has begun */ *state |= HT_ADDBA_REQUESTED_MSK; - /* This is slightly racy because the queue isn't stopped */ start_seq_num = sta->tid_seq[tid]; ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, &sta->sta, tid, &start_seq_num); if (ret) { - /* No need to requeue the packets in the agg queue, since we - * held the tx lock: no packet could be enqueued to the newly - * allocated queue */ - if (hw->ampdu_queues) - ieee80211_ht_agg_queue_remove(local, sta, tid, 0); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA request denied - HW unavailable for" " tid %d\n", tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ *state = HT_AGG_STATE_IDLE; - goto err_unlock_queue; + goto err_free; } + sta->tid_to_tx_q[tid] = qn; - /* Will put all the packets in the new SW queue */ - if (hw->ampdu_queues) - ieee80211_requeue(local, ieee802_1d_to_ac[tid]); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -322,7 +362,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.dialog_token_allocator; sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; - ieee80211_send_addba_request(sta->sdata, ra, tid, sta->ampdu_mlme.tid_tx[tid]->dialog_token, sta->ampdu_mlme.tid_tx[tid]->ssn, @@ -334,15 +373,24 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); #endif - goto exit; + goto unlock; -err_unlock_queue: + err_free: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - ret = -EBUSY; -err_unlock_sta: + err_return_queue: + if (qn >= 0) { + /* We failed, so start queue again right away. */ + ieee80211_wake_queue_by_reason(hw, hw->queues + qn, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + /* give queue back to pool */ + spin_lock(&local->queue_stop_reason_lock); + local->ampdu_ac_queue[qn] = -1; + spin_unlock(&local->queue_stop_reason_lock); + } + err_unlock_sta: spin_unlock_bh(&sta->lock); -exit: + unlock: rcu_read_unlock(); return ret; } @@ -375,7 +423,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) state = &sta->ampdu_mlme.tid_state_tx[tid]; spin_lock_bh(&sta->lock); - if (!(*state & HT_ADDBA_REQUESTED_MSK)) { + if (WARN_ON(!(*state & HT_ADDBA_REQUESTED_MSK))) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", *state); @@ -385,7 +433,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) return; } - WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK); + if (WARN_ON(*state & HT_ADDBA_DRV_READY_MSK)) + goto out; *state |= HT_ADDBA_DRV_READY_MSK; @@ -393,9 +442,18 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); #endif - if (hw->ampdu_queues) - ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); + if (hw->ampdu_queues) { + /* + * Wake up this queue, we stopped it earlier, + * this will in turn wake the entire AC. + */ + ieee80211_wake_queue_by_reason(hw, + hw->queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } } + + out: spin_unlock_bh(&sta->lock); rcu_read_unlock(); } @@ -485,7 +543,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) struct ieee80211_local *local = hw_to_local(hw); struct sta_info *sta; u8 *state; - int agg_queue; if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG @@ -527,19 +584,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); - if (hw->ampdu_queues) { - agg_queue = sta->tid_to_tx_q[tid]; - ieee80211_ht_agg_queue_remove(local, sta, tid, 1); + spin_lock_bh(&sta->lock); - /* We just requeued the all the frames that were in the - * removed queue, and since we might miss a softirq we do - * netif_schedule_queue. ieee80211_wake_queue is not used - * here as this queue is not necessarily stopped + if (*state & HT_AGG_STATE_INITIATOR_MSK && + hw->ampdu_queues) { + /* + * Wake up this queue, we stopped it earlier, + * this will in turn wake the entire AC. */ - netif_schedule_queue(netdev_get_tx_queue(local->mdev, - agg_queue)); + ieee80211_wake_queue_by_reason(hw, + hw->queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); } - spin_lock_bh(&sta->lock); + *state = HT_AGG_STATE_IDLE; sta->ampdu_mlme.addba_req_num[tid] = 0; kfree(sta->ampdu_mlme.tid_tx[tid]); @@ -613,12 +670,21 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, #endif /* CONFIG_MAC80211_HT_DEBUG */ if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) == WLAN_STATUS_SUCCESS) { + u8 curstate = *state; + *state |= HT_ADDBA_RECEIVED_MSK; - sta->ampdu_mlme.addba_req_num[tid] = 0; - if (*state == HT_AGG_STATE_OPERATIONAL && - local->hw.ampdu_queues) - ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); + if (hw->ampdu_queues && *state != curstate && + *state == HT_AGG_STATE_OPERATIONAL) { + /* + * Wake up this queue, we stopped it earlier, + * this will in turn wake the entire AC. + */ + ieee80211_wake_queue_by_reason(hw, + hw->queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + sta->ampdu_mlme.addba_req_num[tid] = 0; if (local->ops->ampdu_action) { (void)local->ops->ampdu_action(hw, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2cb743ed9f9..e2bbd3f1179 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -564,12 +564,10 @@ enum { enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_DRIVER, IEEE80211_QUEUE_STOP_REASON_PS, - IEEE80211_QUEUE_STOP_REASON_CSA + IEEE80211_QUEUE_STOP_REASON_CSA, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION, }; -/* maximum number of hardware queues we support. */ -#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES) - struct ieee80211_master_priv { struct ieee80211_local *local; }; @@ -582,9 +580,15 @@ struct ieee80211_local { const struct ieee80211_ops *ops; - unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)]; - unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; + /* AC queue corresponding to each AMPDU queue */ + s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES]; + unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES]; + + unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES + + IEEE80211_MAX_AMPDU_QUEUES]; + /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; + struct net_device *mdev; /* wmaster# - "master" 802.11 device */ int open_count; int monitors, cooked_mntrs; @@ -1042,6 +1046,10 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); +void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, + enum queue_stop_reason reason); +void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, + enum queue_stop_reason reason); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 795f8c4a9fa..e9181981adc 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -705,7 +705,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops) { struct ieee80211_local *local; - int priv_size; + int priv_size, i; struct wiphy *wiphy; /* Ensure 32-byte alignment of our private data and hw private data. @@ -779,6 +779,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); + for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++) + local->ampdu_ac_queue[i] = -1; + /* using an s8 won't work with more than that */ + BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127); + sta_info_init(local); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, @@ -872,7 +877,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), "wmaster%d", ieee80211_master_setup, - ieee80211_num_queues(hw)); + hw->queues); if (!mdev) goto fail_mdev_alloc; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 634f65c0130..4ba3c540fcf 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -202,6 +202,18 @@ void sta_info_destroy(struct sta_info *sta) /* Make sure timer won't free the tid_rx struct, see below */ if (tid_rx) tid_rx->shutdown = true; + + /* + * The stop callback cannot find this station any more, but + * it didn't complete its work -- start the queue if necessary + */ + if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK && + sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK && + local->hw.ampdu_queues) + ieee80211_wake_queue_by_reason(&local->hw, + local->hw.queues + sta->tid_to_tx_q[i], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + spin_unlock_bh(&sta->lock); /* @@ -275,8 +287,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, * enable session_timer's data differentiation. refer to * sta_rx_agg_session_timer_expired for useage */ sta->timer_to_tid[i] = i; - /* tid to tx queue: initialize according to HW (0 is valid) */ - sta->tid_to_tx_q[i] = ieee80211_num_queues(&local->hw); + sta->tid_to_tx_q[i] = -1; /* rx */ sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_rx[i] = NULL; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d9653231992..a2921f15787 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -200,7 +200,7 @@ struct sta_ampdu_mlme { * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers - * @tid_to_tx_q: map tid to tx queue + * @tid_to_tx_q: map tid to tx queue (invalid == negative values) * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state @@ -275,7 +275,7 @@ struct sta_info { */ struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[STA_TID_NUM]; - u8 tid_to_tx_q[STA_TID_NUM]; + s8 tid_to_tx_q[STA_TID_NUM]; #ifdef CONFIG_MAC80211_MESH /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 33926831c64..6aca49897d5 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -876,7 +876,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) return TX_CONTINUE; } - /* actual transmit path */ /* @@ -1016,12 +1015,20 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, tx->sta = sta_info_get(local, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) { + unsigned long flags; qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + spin_lock_irqsave(&tx->sta->lock, flags); state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) + if (*state == HT_AGG_STATE_OPERATIONAL) { info->flags |= IEEE80211_TX_CTL_AMPDU; + if (local->hw.ampdu_queues) + skb_set_queue_mapping( + skb, tx->local->hw.queues + + tx->sta->tid_to_tx_q[tid]); + } + spin_unlock_irqrestore(&tx->sta->lock, flags); } if (is_multicast_ether_addr(hdr->addr1)) { @@ -1085,7 +1092,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, int ret, i; if (skb) { - if (netif_subqueue_stopped(local->mdev, skb)) + if (ieee80211_queue_stopped(&local->hw, + skb_get_queue_mapping(skb))) return IEEE80211_TX_PENDING; ret = local->ops->tx(local_to_hw(local), skb); @@ -1101,8 +1109,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, info = IEEE80211_SKB_CB(tx->extra_frag[i]); info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - if (netif_subqueue_stopped(local->mdev, - tx->extra_frag[i])) + if (ieee80211_queue_stopped(&local->hw, + skb_get_queue_mapping(tx->extra_frag[i]))) return IEEE80211_TX_FRAG_AGAIN; ret = local->ops->tx(local_to_hw(local), diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 73c7d7345ab..92ea1770461 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -344,15 +344,36 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - /* we don't need to track ampdu queues */ - if (queue < ieee80211_num_regular_queues(hw)) { - __clear_bit(reason, &local->queue_stop_reasons[queue]); + if (queue >= hw->queues) { + if (local->ampdu_ac_queue[queue - hw->queues] < 0) + return; + + /* + * for virtual aggregation queues, we need to refcount the + * internal mac80211 disable (multiple times!), keep track of + * driver disable _and_ make sure the regular queue is + * actually enabled. + */ + if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) + local->amdpu_ac_stop_refcnt[queue - hw->queues]--; + else + __clear_bit(reason, &local->queue_stop_reasons[queue]); - if (local->queue_stop_reasons[queue] != 0) - /* someone still has this queue stopped */ + if (local->queue_stop_reasons[queue] || + local->amdpu_ac_stop_refcnt[queue - hw->queues]) return; + + /* now go on to treat the corresponding regular queue */ + queue = local->ampdu_ac_queue[queue - hw->queues]; + reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; } + __clear_bit(reason, &local->queue_stop_reasons[queue]); + + if (local->queue_stop_reasons[queue] != 0) + /* someone still has this queue stopped */ + return; + if (test_bit(queue, local->queues_pending)) { set_bit(queue, local->queues_pending_run); tasklet_schedule(&local->tx_pending_tasklet); @@ -361,8 +382,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, } } -static void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) +void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, + enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; @@ -384,15 +405,33 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - /* we don't need to track ampdu queues */ - if (queue < ieee80211_num_regular_queues(hw)) - __set_bit(reason, &local->queue_stop_reasons[queue]); + if (queue >= hw->queues) { + if (local->ampdu_ac_queue[queue - hw->queues] < 0) + return; + + /* + * for virtual aggregation queues, we need to refcount the + * internal mac80211 disable (multiple times!), keep track of + * driver disable _and_ make sure the regular queue is + * actually enabled. + */ + if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) + local->amdpu_ac_stop_refcnt[queue - hw->queues]++; + else + __set_bit(reason, &local->queue_stop_reasons[queue]); + + /* now go on to treat the corresponding regular queue */ + queue = local->ampdu_ac_queue[queue - hw->queues]; + reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; + } + + __set_bit(reason, &local->queue_stop_reasons[queue]); netif_stop_subqueue(local->mdev, queue); } -static void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, - enum queue_stop_reason reason) +void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, + enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; @@ -418,7 +457,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < ieee80211_num_queues(hw); i++) + for (i = 0; i < hw->queues; i++) __ieee80211_stop_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -434,6 +473,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues); int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) { struct ieee80211_local *local = hw_to_local(hw); + unsigned long flags; + + if (queue >= hw->queues) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + queue = local->ampdu_ac_queue[queue - hw->queues]; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if (queue < 0) + return true; + } + return __netif_subqueue_stopped(local->mdev, queue); } EXPORT_SYMBOL(ieee80211_queue_stopped); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index ac71b38f7cb..093a4ab7f28 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -114,9 +114,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) { struct ieee80211_master_priv *mpriv = netdev_priv(dev); struct ieee80211_local *local = mpriv->local; - struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct sta_info *sta; u16 queue; u8 tid; @@ -124,29 +122,11 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) if (unlikely(queue >= local->hw.queues)) queue = local->hw.queues - 1; - if (skb->requeue) { - if (!hw->ampdu_queues) - return queue; - - rcu_read_lock(); - sta = sta_info_get(local, hdr->addr1); - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - if (sta) { - int ampdu_queue = sta->tid_to_tx_q[tid]; - - if ((ampdu_queue < ieee80211_num_queues(hw)) && - test_bit(ampdu_queue, local->queue_pool)) - queue = ampdu_queue; - } - rcu_read_unlock(); - - return queue; - } - - /* Now we know the 1d priority, fill in the QoS header if - * there is one. + /* + * Now we know the 1d priority, fill in the QoS header if + * there is one (and we haven't done this before). */ - if (ieee80211_is_data_qos(hdr->frame_control)) { + if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy = 0; tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; @@ -156,140 +136,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) /* qos header is 2 bytes, second reserved */ *p++ = ack_policy | tid; *p = 0; - - if (!hw->ampdu_queues) - return queue; - - rcu_read_lock(); - - sta = sta_info_get(local, hdr->addr1); - if (sta) { - int ampdu_queue = sta->tid_to_tx_q[tid]; - - if ((ampdu_queue < ieee80211_num_queues(hw)) && - test_bit(ampdu_queue, local->queue_pool)) - queue = ampdu_queue; - } - - rcu_read_unlock(); } return queue; } - -int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, - struct sta_info *sta, u16 tid) -{ - int i; - - /* XXX: currently broken due to cb/requeue use */ - return -EPERM; - - /* prepare the filter and save it for the SW queue - * matching the received HW queue */ - - if (!local->hw.ampdu_queues) - return -EPERM; - - /* try to get a Qdisc from the pool */ - for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++) - if (!test_and_set_bit(i, local->queue_pool)) { - ieee80211_stop_queue(local_to_hw(local), i); - sta->tid_to_tx_q[tid] = i; - - /* IF there are already pending packets - * on this tid first we need to drain them - * on the previous queue - * since HT is strict in order */ -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - printk(KERN_DEBUG "allocated aggregation queue" - " %d tid %d addr %pM pool=0x%lX\n", - i, tid, sta->sta.addr, - local->queue_pool[0]); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - return 0; - } - - return -EAGAIN; -} - -/** - * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock - */ -void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, - struct sta_info *sta, u16 tid, - u8 requeue) -{ - int agg_queue = sta->tid_to_tx_q[tid]; - struct ieee80211_hw *hw = &local->hw; - - /* return the qdisc to the pool */ - clear_bit(agg_queue, local->queue_pool); - sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw); - - if (requeue) { - ieee80211_requeue(local, agg_queue); - } else { - struct netdev_queue *txq; - spinlock_t *root_lock; - struct Qdisc *q; - - txq = netdev_get_tx_queue(local->mdev, agg_queue); - q = rcu_dereference(txq->qdisc); - root_lock = qdisc_lock(q); - - spin_lock_bh(root_lock); - qdisc_reset(q); - spin_unlock_bh(root_lock); - } -} - -void ieee80211_requeue(struct ieee80211_local *local, int queue) -{ - struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue); - struct sk_buff_head list; - spinlock_t *root_lock; - struct Qdisc *qdisc; - u32 len; - - rcu_read_lock_bh(); - - qdisc = rcu_dereference(txq->qdisc); - if (!qdisc || !qdisc->dequeue) - goto out_unlock; - - skb_queue_head_init(&list); - - root_lock = qdisc_root_lock(qdisc); - spin_lock(root_lock); - for (len = qdisc->q.qlen; len > 0; len--) { - struct sk_buff *skb = qdisc->dequeue(qdisc); - - if (skb) - __skb_queue_tail(&list, skb); - } - spin_unlock(root_lock); - - for (len = list.qlen; len > 0; len--) { - struct sk_buff *skb = __skb_dequeue(&list); - u16 new_queue; - - BUG_ON(!skb); - new_queue = ieee80211_select_queue(local->mdev, skb); - skb_set_queue_mapping(skb, new_queue); - - txq = netdev_get_tx_queue(local->mdev, new_queue); - - - qdisc = rcu_dereference(txq->qdisc); - root_lock = qdisc_root_lock(qdisc); - - spin_lock(root_lock); - qdisc_enqueue_root(skb, qdisc); - spin_unlock(root_lock); - } - -out_unlock: - rcu_read_unlock_bh(); -} diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index bc62f28a4d3..7520d2e014d 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -21,11 +21,5 @@ extern const int ieee802_1d_to_ac[8]; u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb); -int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, - struct sta_info *sta, u16 tid); -void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, - struct sta_info *sta, u16 tid, - u8 requeue); -void ieee80211_requeue(struct ieee80211_local *local, int queue); #endif /* _WME_H */ -- cgit v1.2.3-70-g09d2 From 81cb7623ad3b408f871fa36b774fc20d8dfccac0 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 12 Feb 2009 11:38:37 +0530 Subject: mac80211: Extend the rate control API with an update callback The AP can switch dynamically between 20/40 Mhz channel width, in which case we switch the local operating channel, but the rate control algorithm is not notified. This patch adds a new callback to indicate such changes to the RC algorithm. Currently, HT channel width change is notified, but this callback can be used to indicate any new requirements that might come up later on. Signed-off-by: Sujith Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 13 +++++++++++++ net/mac80211/ht.c | 13 +++++++++++++ net/mac80211/rate.h | 12 ++++++++++++ 3 files changed, 38 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 31fd8bab217..e01c63aad66 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1974,6 +1974,16 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, /* Rate control API */ +/** + * enum rate_control_changed - flags to indicate which parameter changed + * + * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have + * changed, rate control algorithm can update its internal state if needed. + */ +enum rate_control_changed { + IEEE80211_RC_HT_CHANGED = BIT(0) +}; + /** * struct ieee80211_tx_rate_control - rate control information for/from RC algo * @@ -2010,6 +2020,9 @@ struct rate_control_ops { void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp); void (*rate_init)(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta); + void (*rate_update)(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, + void *priv_sta, u32 changed); void (*free_sta)(void *priv, struct ieee80211_sta *sta, void *priv_sta); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 69b6e9a4df3..4e3c72f20de 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -17,6 +17,7 @@ #include #include #include "ieee80211_i.h" +#include "rate.h" void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, @@ -93,7 +94,9 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss_ht_conf ht; + struct sta_info *sta; u32 changed = 0; bool enable_ht = true, ht_changed; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; @@ -136,6 +139,16 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, if (ht_changed) { /* channel_type change automatically detected */ ieee80211_hw_config(local, 0); + + rcu_read_lock(); + + sta = sta_info_get(local, ifmgd->bssid); + if (sta) + rate_control_rate_update(local, sband, sta, + IEEE80211_RC_HT_CHANGED); + + rcu_read_unlock(); + } /* disable HT */ diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 928da625e28..b9164c9a956 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -62,6 +62,18 @@ static inline void rate_control_rate_init(struct sta_info *sta) ref->ops->rate_init(ref->priv, sband, ista, priv_sta); } +static inline void rate_control_rate_update(struct ieee80211_local *local, + struct ieee80211_supported_band *sband, + struct sta_info *sta, u32 changed) +{ + struct rate_control_ref *ref = local->rate_ctrl; + struct ieee80211_sta *ista = &sta->sta; + void *priv_sta = sta->rate_ctrl_priv; + + if (ref->ops->rate_update) + ref->ops->rate_update(ref->priv, sband, ista, + priv_sta, changed); +} static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, struct ieee80211_sta *sta, -- cgit v1.2.3-70-g09d2 From 70692ad2923a379e0a10f9ec2ad93fbbe084cc46 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 16 Feb 2009 19:39:13 +0200 Subject: nl80211: Optional IEs into scan request This extends the NL80211_CMD_TRIGGER_SCAN command to allow applications to specify a set of information element(s) to be added into Probe Request frames with NL80211_ATTR_IE. This provides support for the MLME-SCAN.request primitive parameter VendorSpecificInfo and can be used, e.g., to implement WPS scanning. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 4 ++++ net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/mlme.c | 8 +++++--- net/mac80211/scan.c | 3 ++- net/mac80211/util.c | 7 +++++-- net/wireless/nl80211.c | 21 ++++++++++++++++++++- 6 files changed, 38 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c0d1f5b708c..33f43b0d08f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -525,6 +525,8 @@ struct cfg80211_ssid { * @n_ssids: number of SSIDs * @channels: channels to scan on. * @n_channels: number of channels for each band + * @ie: optional information element(s) to add into Probe Request or %NULL + * @ie_len: length of ie in octets * @wiphy: the wiphy this was for * @ifidx: the interface index */ @@ -533,6 +535,8 @@ struct cfg80211_scan_request { int n_ssids; struct ieee80211_channel **channels; u32 n_channels; + u8 *ie; + size_t ie_len; /* internal */ struct wiphy *wiphy; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 27d56414019..d06c75720ce 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1093,7 +1093,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u8 *extra, size_t extra_len, const u8 *bssid, int encrypt); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, - u8 *ssid, size_t ssid_len); + u8 *ssid, size_t ssid_len, + u8 *ie, size_t ie_len); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ec5a0900cba..5a4977936f6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -716,7 +716,7 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, - ifmgd->ssid, ifmgd->ssid_len); + ifmgd->ssid, ifmgd->ssid_len, NULL, 0); mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); } @@ -946,7 +946,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) } else ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len); + ifmgd->ssid_len, + NULL, 0); ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL; } else { ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; @@ -955,7 +956,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) ifmgd->last_probe = jiffies; ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len); + ifmgd->ssid_len, + NULL, 0); } } } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 08a1fc27ca1..c063f820426 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -338,7 +338,8 @@ void ieee80211_scan_work(struct work_struct *work) ieee80211_send_probe_req( sdata, NULL, local->scan_req->ssids[i].ssid, - local->scan_req->ssids[i].ssid_len); + local->scan_req->ssids[i].ssid_len, + local->scan_req->ie, local->scan_req->ie_len); next_delay = IEEE80211_CHANNEL_TIME; break; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index dee17e5cbb8..e0431a1d218 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -884,7 +884,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, } void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, - u8 *ssid, size_t ssid_len) + u8 *ssid, size_t ssid_len, + u8 *ie, size_t ie_len) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -903,7 +904,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + - extra_preq_ie_len); + ie_len + extra_preq_ie_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -950,6 +951,8 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, *pos = rate->bitrate / 5; } + if (ie) + memcpy(skb_put(skb, ie_len), ie, ie_len); if (extra_preq_ie) memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie, extra_preq_ie_len); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 298a4de5994..67b18b3a93a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2286,6 +2286,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels = 0, i; enum ieee80211_band band; + size_t ie_len; err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) @@ -2327,9 +2328,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } + if (info->attrs[NL80211_ATTR_IE]) + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + else + ie_len = 0; + request = kzalloc(sizeof(*request) + sizeof(*ssid) * n_ssids - + sizeof(channel) * n_channels, GFP_KERNEL); + + sizeof(channel) * n_channels + + ie_len, GFP_KERNEL); if (!request) { err = -ENOMEM; goto out_unlock; @@ -2340,6 +2347,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (n_ssids) request->ssids = (void *)(request->channels + n_channels); request->n_ssids = n_ssids; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssids); + else + request->ie = (void *)(request->channels + n_channels); + } if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { /* user specified, bail out if channel not found */ @@ -2380,6 +2393,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_IE]) { + request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]), + request->ie_len); + } + request->ifidx = dev->ifindex; request->wiphy = &drv->wiphy; -- cgit v1.2.3-70-g09d2 From 98c8a60a04316e94ccea8221cf16768ce91bd214 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 17 Feb 2009 13:24:57 +0200 Subject: nl80211: Provide access to STA TX/RX packet counters The TX/RX packet counters are needed to fill in RADIUS Accounting attributes Acct-Output-Packets and Acct-Input-Packets. We already collect the needed information, but only the TX/RX bytes were previously exposed through nl80211. Allow applications to fetch the packet counters, too, to provide more complete support for accounting. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 8 ++++++++ net/mac80211/cfg.c | 4 ++++ net/wireless/nl80211.c | 6 ++++++ 4 files changed, 23 insertions(+) (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 8802d1bda38..f6e56370ea6 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -526,6 +526,9 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, @@ -537,6 +540,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_PLINK_STATE, NL80211_STA_INFO_SIGNAL, NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 33f43b0d08f..8dcc4644403 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -178,6 +178,8 @@ struct station_parameters { * @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) + * @STATION_INFO_RX_PACKETS: @rx_packets filled + * @STATION_INFO_TX_PACKETS: @tx_packets filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -188,6 +190,8 @@ enum station_info_flags { STATION_INFO_PLINK_STATE = 1<<5, STATION_INFO_SIGNAL = 1<<6, STATION_INFO_TX_BITRATE = 1<<7, + STATION_INFO_RX_PACKETS = 1<<8, + STATION_INFO_TX_PACKETS = 1<<9, }; /** @@ -235,6 +239,8 @@ struct rate_info { * @plink_state: mesh peer link state * @signal: signal strength of last received packet in dBm * @txrate: current unicast bitrate to this station + * @rx_packets: packets received from this station + * @tx_packets: packets transmitted to this station */ struct station_info { u32 filled; @@ -246,6 +252,8 @@ struct station_info { u8 plink_state; s8 signal; struct rate_info txrate; + u32 rx_packets; + u32 tx_packets; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f453bb7c564..c43129efc3b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -341,11 +341,15 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | + STATION_INFO_RX_PACKETS | + STATION_INFO_TX_PACKETS | STATION_INFO_TX_BITRATE; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; sinfo->tx_bytes = sta->tx_bytes; + sinfo->rx_packets = sta->rx_packets; + sinfo->tx_packets = sta->tx_packets; if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { sinfo->filled |= STATION_INFO_SIGNAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 67b18b3a93a..badccf98074 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1206,6 +1206,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, nla_nest_end(msg, txrate); } + if (sinfo->filled & STATION_INFO_RX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, + sinfo->rx_packets); + if (sinfo->filled & STATION_INFO_TX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, + sinfo->tx_packets); nla_nest_end(msg, sinfoattr); return genlmsg_end(msg, hdr); -- cgit v1.2.3-70-g09d2 From 77965c970d7da9c9b6349ff2b1d9adecf54c403b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Feb 2009 18:45:06 +0100 Subject: cfg80211: clean up signal type It wasn't a good idea to make the signal type a per-BSS option, although then it is closer to the actual value. Move it to be a per-wiphy setting, update mac80211 to match. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 8 +++----- include/net/wireless.h | 3 +++ net/mac80211/main.c | 5 +++++ net/mac80211/scan.c | 11 +++-------- net/wireless/nl80211.c | 2 +- net/wireless/scan.c | 21 +++++++++------------ 6 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8dcc4644403..e0312746a8c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -577,8 +577,7 @@ enum cfg80211_signal_type { * @information_elements: the information elements (Note that there * is no guarantee that these are well-formed!) * @len_information_elements: total length of the information elements - * @signal: signal strength value - * @signal_type: signal type + * @signal: signal strength value (type depends on the wiphy's signal_type) * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ @@ -593,7 +592,6 @@ struct cfg80211_bss { size_t len_information_elements; s32 signal; - enum cfg80211_signal_type signal_type; void (*free_priv)(struct cfg80211_bss *bss); u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); @@ -782,6 +780,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); * * @wiphy: the wiphy reporting the BSS * @bss: the found BSS + * @signal: the signal strength, type depends on the wiphy's signal_type * @gfp: context flags * * This informs cfg80211 that BSS information was found and @@ -791,8 +790,7 @@ struct cfg80211_bss* cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, - s32 signal, enum cfg80211_signal_type sigtype, - gfp_t gfp); + s32 signal, gfp_t gfp); struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, diff --git a/include/net/wireless.h b/include/net/wireless.h index 1c6285eb166..d815aa8b453 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -200,6 +200,7 @@ struct ieee80211_supported_band { * the regulatory_hint() API. This can be used by the driver * on the reg_notifier() if it chooses to ignore future * regulatory domain changes caused by other drivers. + * @signal_type: signal type reported in &struct cfg80211_bss. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -213,6 +214,8 @@ struct wiphy { bool custom_regulatory; bool strict_regulatory; + enum cfg80211_signal_type signal_type; + int bss_priv_size; u8 max_scan_ssids; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index fce9d08986e..f38db4d37e5 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -861,6 +861,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* mac80211 always supports monitor */ local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) + local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index c063f820426..23f4de27474 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -63,20 +63,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local, { struct ieee80211_bss *bss; int clen; - enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE; s32 signal = 0; - if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { - sigtype = CFG80211_SIGNAL_TYPE_MBM; + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) signal = rx_status->signal * 100; - } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { - sigtype = CFG80211_SIGNAL_TYPE_UNSPEC; + else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) signal = (rx_status->signal * 100) / local->hw.max_signal; - } bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel, - mgmt, len, signal, sigtype, - GFP_ATOMIC); + mgmt, len, signal, GFP_ATOMIC); if (!bss) return NULL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 245fddcc77c..a7e751edc73 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2457,7 +2457,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); - switch (res->signal_type) { + switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); break; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 01c136d98c5..60600657b65 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -370,7 +370,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, found->pub.beacon_interval = res->pub.beacon_interval; found->pub.tsf = res->pub.tsf; found->pub.signal = res->pub.signal; - found->pub.signal_type = res->pub.signal_type; found->pub.capability = res->pub.capability; found->ts = res->ts; kref_put(&res->ref, bss_release); @@ -392,8 +391,7 @@ struct cfg80211_bss * cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, - s32 signal, enum cfg80211_signal_type sigtype, - gfp_t gfp) + s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; size_t ielen = len - offsetof(struct ieee80211_mgmt, @@ -401,7 +399,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, bool overwrite; size_t privsz = wiphy->bss_priv_size; - if (WARN_ON(sigtype == NL80211_BSS_SIGNAL_UNSPEC && + if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && (signal < 0 || signal > 100))) return NULL; @@ -415,7 +413,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); res->pub.channel = channel; - res->pub.signal_type = sigtype; res->pub.signal = signal; res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); @@ -607,9 +604,9 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start) } static char * -ieee80211_bss(struct iw_request_info *info, - struct cfg80211_internal_bss *bss, - char *current_ev, char *end_buf) +ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, + struct cfg80211_internal_bss *bss, char *current_ev, + char *end_buf) { struct iw_event iwe; u8 *buf, *cfg, *p; @@ -638,13 +635,13 @@ ieee80211_bss(struct iw_request_info *info, current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); - if (bss->pub.signal_type != CFG80211_SIGNAL_TYPE_NONE) { + if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_QUAL_UPDATED; - switch (bss->pub.signal_type) { + switch (wiphy->signal_type) { case CFG80211_SIGNAL_TYPE_MBM: sig = bss->pub.signal / 100; iwe.u.qual.level = sig; @@ -823,8 +820,8 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *dev, spin_unlock_bh(&dev->bss_lock); return -E2BIG; } - current_ev = ieee80211_bss(info, bss, - current_ev, end_buf); + current_ev = ieee80211_bss(&dev->wiphy, info, bss, + current_ev, end_buf); } spin_unlock_bh(&dev->bss_lock); return current_ev - buf; -- cgit v1.2.3-70-g09d2 From 4aa188e1a868d25c5b93e48e5d29bbd0f9d3bc3a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Feb 2009 19:32:08 +0100 Subject: mac80211/cfg80211: move iwrange handler to cfg80211 The previous patch made cfg80211 generally aware of the signal type a given hardware will give, so now it can implement SIOCGIWRANGE itself, removing more wext stuff from mac80211. Might need to be a little more parametrized once we have more hardware using cfg80211 and new hardware capabilities. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 3 + net/mac80211/wext.c | 135 ++++++--------------------------------------- net/wireless/wext-compat.c | 97 ++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 119 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e0312746a8c..43ac90df416 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -765,6 +765,9 @@ int cfg80211_wext_siwscan(struct net_device *dev, int cfg80211_wext_giwscan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra); +int cfg80211_wext_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); /** * cfg80211_scan_done - notify that scan finished diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index a8d4b617191..f6924fc065d 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -144,124 +144,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, return -EOPNOTSUPP; } -static u8 ieee80211_get_wstats_flags(struct ieee80211_local *local) -{ - u8 wstats_flags = 0; - - wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | - IEEE80211_HW_SIGNAL_DBM) ? - IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; - wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ? - IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID; - if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) - wstats_flags |= IW_QUAL_DBM; - - return wstats_flags; -} - -static int ieee80211_ioctl_giwrange(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct iw_range *range = (struct iw_range *) extra; - enum ieee80211_band band; - int c = 0; - - data->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 21; - range->retry_capa = IW_RETRY_LIMIT; - range->retry_flags = IW_RETRY_LIMIT; - range->min_retry = 0; - range->max_retry = 255; - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - range->max_encoding_tokens = NUM_DEFAULT_KEYS; - - /* cfg80211 requires this, and enforces 0..100 */ - if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) - range->max_qual.level = 100; - else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) - range->max_qual.level = -110; - else - range->max_qual.level = 0; - - if (local->hw.flags & IEEE80211_HW_NOISE_DBM) - range->max_qual.noise = -110; - else - range->max_qual.noise = 0; - - range->max_qual.updated = ieee80211_get_wstats_flags(local); - - if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { - /* - * cfg80211 assumes -110 to -40 dBm and clamps to that range - * for qual.qual, so tell userspace this is what we give it - * but take into account that we have to start from 0. - */ - range->max_qual.qual = 70; - range->avg_qual.qual = 35; - } else { - /* - * cfg80211 just uses the level value for qual too, and it - * requires the level value to be 0 .. 100. - */ - range->max_qual.qual = 100; - range->avg_qual.qual = 50; - } - /* not always true but better than nothing */ - range->avg_qual.level = range->max_qual.level / 2; - range->avg_qual.noise = range->max_qual.noise / 2; - range->avg_qual.updated = ieee80211_get_wstats_flags(local); - - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - - - for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { - int i; - struct ieee80211_supported_band *sband; - - sband = local->hw.wiphy->bands[band]; - - if (!sband) - continue; - - for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { - struct ieee80211_channel *chan = &sband->channels[i]; - - if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { - range->freq[c].i = - ieee80211_frequency_to_channel( - chan->center_freq); - range->freq[c].m = chan->center_freq; - range->freq[c].e = 6; - c++; - } - } - } - range->num_channels = c; - range->num_frequency = c; - - IW_EVENT_CAPA_SET_KERNEL(range->event_capa); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - - range->scan_capa |= IW_SCAN_CAPA_ESSID; - - return 0; -} - - static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) @@ -1004,6 +886,21 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, return ret; } +static u8 ieee80211_get_wstats_flags(struct ieee80211_local *local) +{ + u8 wstats_flags = 0; + + wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | + IEEE80211_HW_SIGNAL_DBM) ? + IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; + wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ? + IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID; + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + wstats_flags |= IW_QUAL_DBM; + + return wstats_flags; +} + /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { @@ -1149,7 +1046,7 @@ static const iw_handler ieee80211_handler[] = (iw_handler) NULL, /* SIOCSIWSENS */ (iw_handler) NULL, /* SIOCGIWSENS */ (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ - (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 58e489fd4ae..b84a9b4fe96 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -137,3 +137,100 @@ int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, return 0; } EXPORT_SYMBOL(cfg80211_wext_giwmode); + + +int cfg80211_wext_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_range *range = (struct iw_range *) extra; + enum ieee80211_band band; + int c = 0; + + if (!wdev) + return -EOPNOTSUPP; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 21; + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + switch (wdev->wiphy->signal_type) { + case CFG80211_SIGNAL_TYPE_NONE: + break; + case CFG80211_SIGNAL_TYPE_MBM: + range->max_qual.level = -110; + range->max_qual.qual = 70; + range->avg_qual.qual = 35; + range->max_qual.updated |= IW_QUAL_DBM; + range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; + range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + range->max_qual.level = 100; + range->max_qual.qual = 100; + range->avg_qual.qual = 50; + range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; + range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; + break; + } + + range->avg_qual.level = range->max_qual.level / 2; + range->avg_qual.noise = range->max_qual.noise / 2; + range->avg_qual.updated = range->max_qual.updated; + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + + for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { + int i; + struct ieee80211_supported_band *sband; + + sband = wdev->wiphy->bands[band]; + + if (!sband) + continue; + + for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + + if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { + range->freq[c].i = + ieee80211_frequency_to_channel( + chan->center_freq); + range->freq[c].m = chan->center_freq; + range->freq[c].e = 6; + c++; + } + } + } + range->num_channels = c; + range->num_frequency = c; + + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + + range->scan_capa |= IW_SCAN_CAPA_ESSID; + + return 0; +} +EXPORT_SYMBOL(cfg80211_wext_giwrange); -- cgit v1.2.3-70-g09d2 From 80e775bf08f1915870fbb0c1c7a45a3fdc291721 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 20 Feb 2009 15:37:03 +0100 Subject: mac80211: Add software scan notifiers This adds optional notifier functions for software scan. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- include/net/mac80211.h | 8 ++++++++ net/mac80211/scan.c | 5 +++++ 2 files changed, 13 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e01c63aad66..12a52efcd0d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1324,6 +1324,12 @@ enum ieee80211_ampdu_mlme_action { * because the hardware is turned off! Anything else is a bug! * Returns a negative error code which will be seen in userspace. * + * @sw_scan_start: Notifier function that is called just before a software scan + * is started. Can be NULL, if the driver doesn't need this notification. + * + * @sw_scan_complete: Notifier function that is called just after a software scan + * finished. Can be NULL, if the driver doesn't need this notification. + * * @get_stats: Return low-level statistics. * Returns zero if statistics are available. * @@ -1403,6 +1409,8 @@ struct ieee80211_ops { u32 iv32, u16 *phase1key); int (*hw_scan)(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); + void (*sw_scan_start)(struct ieee80211_hw *hw); + void (*sw_scan_complete)(struct ieee80211_hw *hw); int (*get_stats)(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 23f4de27474..0e81e1633a6 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -245,6 +245,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); + if (local->ops->sw_scan_complete) + local->ops->sw_scan_complete(local_to_hw(local)); + mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!netif_running(sdata->dev)) @@ -395,6 +398,8 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, } local->sw_scanning = true; + if (local->ops->sw_scan_start) + local->ops->sw_scan_start(local_to_hw(local)); mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { -- cgit v1.2.3-70-g09d2 From 806a9e39670be4f1f861c346ec102a79e81b90c3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Feb 2009 00:04:26 -0500 Subject: cfg80211: make regulatory_request use wiphy_idx instead of wiphy We do this so later on we can move the pending requests onto a workqueue. By using the wiphy_idx instead of the wiphy we can later easily check if the wiphy has disappeared or not. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/cfg80211.h | 6 ++--- net/wireless/core.c | 30 ++++++++++++++++++++--- net/wireless/core.h | 12 ++++++++++ net/wireless/reg.c | 64 +++++++++++++++++++++++++++++++------------------- 4 files changed, 82 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 43ac90df416..bb9129fc61c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -383,9 +383,9 @@ enum environment_cap { }; /** - * struct regulatory_request - receipt of last regulatory request + * struct regulatory_request - used to keep track of regulatory requests * - * @wiphy: this is set if this request's initiator is + * @wiphy_idx: this is set if this request's initiator is * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This * can be used by the wireless core to deal with conflicts * and potentially inform users of which devices specifically @@ -406,7 +406,7 @@ enum environment_cap { * indoor, or if it doesn't matter */ struct regulatory_request { - struct wiphy *wiphy; + int wiphy_idx; enum reg_set_by initiator; char alpha2[2]; bool intersect; diff --git a/net/wireless/core.c b/net/wireless/core.c index e347093ccc7..b1a354b7fc0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -40,9 +40,8 @@ DEFINE_MUTEX(cfg80211_mutex); /* for debugfs */ static struct dentry *ieee80211_debugfs_dir; -/* requires cfg80211_drv_mutex to be held! */ -static struct cfg80211_registered_device * -cfg80211_drv_by_wiphy_idx(int wiphy_idx) +/* requires cfg80211_mutex to be held! */ +struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) { struct cfg80211_registered_device *result = NULL, *drv; @@ -61,6 +60,31 @@ cfg80211_drv_by_wiphy_idx(int wiphy_idx) return result; } +int get_wiphy_idx(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *drv; + if (!wiphy) + return WIPHY_IDX_STALE; + drv = wiphy_to_dev(wiphy); + return drv->wiphy_idx; +} + +/* requires cfg80211_drv_mutex to be held! */ +struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) +{ + struct cfg80211_registered_device *drv; + + if (!wiphy_idx_valid(wiphy_idx)) + return NULL; + + assert_cfg80211_lock(); + + drv = cfg80211_drv_by_wiphy_idx(wiphy_idx); + if (!drv) + return NULL; + return &drv->wiphy; +} + /* requires cfg80211_mutex to be held! */ static struct cfg80211_registered_device * __cfg80211_drv_from_info(struct genl_info *info) diff --git a/net/wireless/core.h b/net/wireless/core.h index 982cc6be348..cd8e6e3ef11 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -79,6 +79,12 @@ static inline void assert_cfg80211_lock(void) BUG_ON(!mutex_is_locked(&cfg80211_mutex)); } +/* + * You can use this to mark a wiphy_idx as not having an associated wiphy. + * It guarantees cfg80211_drv_by_wiphy_idx(wiphy_idx) will return NULL + */ +#define WIPHY_IDX_STALE -1 + struct cfg80211_internal_bss { struct list_head list; struct rb_node rbn; @@ -88,6 +94,9 @@ struct cfg80211_internal_bss { struct cfg80211_bss pub; }; +struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); +int get_wiphy_idx(struct wiphy *wiphy); + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. @@ -111,6 +120,9 @@ struct cfg80211_internal_bss { extern struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info); +/* requires cfg80211_drv_mutex to be held! */ +struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); + /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e49ac9b2ada..d44f3b5481a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -831,9 +831,12 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, const struct ieee80211_power_rule *power_rule = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; + struct wiphy *request_wiphy; assert_cfg80211_lock(); + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; @@ -881,8 +884,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, power_rule = ®_rule->power_rule; if (last_request->initiator == REGDOM_SET_BY_DRIVER && - last_request->wiphy && last_request->wiphy == wiphy && - last_request->wiphy->strict_regulatory) { + request_wiphy && request_wiphy == wiphy && + request_wiphy->strict_regulatory) { /* This gaurantees the driver's requested regulatory domain * will always be used as a base for further regulatory * settings */ @@ -1046,6 +1049,7 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, const char *alpha2) { + struct wiphy *last_wiphy = NULL; assert_cfg80211_lock(); @@ -1059,10 +1063,13 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, case REGDOM_SET_BY_CORE: return -EINVAL; case REGDOM_SET_BY_COUNTRY_IE: + + last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + if (unlikely(!is_an_alpha2(alpha2))) return -EINVAL; if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { - if (last_request->wiphy != wiphy) { + if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different * different Country IE alpha2s. We could @@ -1163,7 +1170,7 @@ new_request: request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = set_by; - request->wiphy = wiphy; + request->wiphy_idx = get_wiphy_idx(wiphy); request->intersect = intersect; request->country_ie_checksum = country_ie_checksum; request->country_ie_env = env; @@ -1226,11 +1233,16 @@ EXPORT_SYMBOL(regulatory_hint); static bool reg_same_country_ie_hint(struct wiphy *wiphy, u32 country_ie_checksum) { + struct wiphy *request_wiphy; + assert_cfg80211_lock(); - if (!last_request->wiphy) + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + + if (!request_wiphy) return false; - if (likely(last_request->wiphy != wiphy)) + + if (likely(request_wiphy != wiphy)) return !country_ie_integrity_changes(country_ie_checksum); /* We should not have let these through at this point, they * should have been picked up earlier by the first alpha2 check @@ -1278,14 +1290,15 @@ void regulatory_hint_11d(struct wiphy *wiphy, /* We will run this for *every* beacon processed for the BSSID, so * we optimize an early check to exit out early if we don't have to * do anything */ - if (likely(last_request->wiphy)) { + if (likely(wiphy_idx_valid(last_request->wiphy_idx))) { struct cfg80211_registered_device *drv_last_ie; - drv_last_ie = wiphy_to_dev(last_request->wiphy); + drv_last_ie = + cfg80211_drv_by_wiphy_idx(last_request->wiphy_idx); /* Lets keep this simple -- we trust the first AP * after we intersect with CRDA */ - if (likely(last_request->wiphy == wiphy)) { + if (likely(&drv_last_ie->wiphy == wiphy)) { /* Ignore IEs coming in on this wiphy with * the same alpha2 and environment cap */ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, @@ -1377,13 +1390,12 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) { if (is_intersected_alpha2(rd->alpha2)) { - struct wiphy *wiphy = NULL; - struct cfg80211_registered_device *drv; if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { - if (last_request->wiphy) { - wiphy = last_request->wiphy; - drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *drv; + drv = cfg80211_drv_by_wiphy_idx( + last_request->wiphy_idx); + if (drv) { printk(KERN_INFO "cfg80211: Current regulatory " "domain updated by AP to: %c%c\n", drv->country_ie_alpha2[0], @@ -1449,7 +1461,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) { const struct ieee80211_regdomain *intersected_rd = NULL; struct cfg80211_registered_device *drv = NULL; - struct wiphy *wiphy = NULL; + struct wiphy *request_wiphy; /* Some basic sanity checks first */ if (is_world_regdom(rd->alpha2)) { @@ -1477,8 +1489,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; } - wiphy = last_request->wiphy; - /* Now lets set the regulatory domain, update all driver channels * and finally inform them of what we have done, in case they want * to review or adjust their own settings based on their own @@ -1494,6 +1504,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; } + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + if (!last_request->intersect) { int r; @@ -1506,9 +1518,9 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* For a driver hint, lets copy the regulatory domain the * driver wanted to the wiphy to deal with conflicts */ - BUG_ON(last_request->wiphy->regd); + BUG_ON(request_wiphy->regd); - r = reg_copy_regd(&last_request->wiphy->regd, rd); + r = reg_copy_regd(&request_wiphy->regd, rd); if (r) return r; @@ -1529,7 +1541,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ if (last_request->initiator == REGDOM_SET_BY_DRIVER) - last_request->wiphy->regd = rd; + request_wiphy->regd = rd; else kfree(rd); @@ -1569,7 +1581,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!intersected_rd) return -EINVAL; - drv = wiphy_to_dev(wiphy); + drv = wiphy_to_dev(request_wiphy); drv->country_ie_alpha2[0] = rd->alpha2[0]; drv->country_ie_alpha2[1] = rd->alpha2[1]; @@ -1618,14 +1630,18 @@ int set_regdom(const struct ieee80211_regdomain *rd) /* Caller must hold cfg80211_mutex */ void reg_device_remove(struct wiphy *wiphy) { + struct wiphy *request_wiphy; + assert_cfg80211_lock(); + request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + kfree(wiphy->regd); - if (!last_request || !last_request->wiphy) + if (!last_request || !request_wiphy) return; - if (last_request->wiphy != wiphy) + if (request_wiphy != wiphy) return; - last_request->wiphy = NULL; + last_request->wiphy_idx = WIPHY_IDX_STALE; last_request->country_ie_env = ENVIRON_ANY; } -- cgit v1.2.3-70-g09d2 From fe33eb390854886e1fd5d4835d833b80d145aafb Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Feb 2009 00:04:30 -0500 Subject: cfg80211: move all regulatory hints to workqueue All regulatory hints (core, driver, userspace and 11d) are now processed in a workqueue. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 8 +- drivers/net/wireless/zd1211rw/zd_mac.c | 6 +- include/net/cfg80211.h | 2 + include/net/wireless.h | 9 +- net/wireless/nl80211.c | 30 ++---- net/wireless/reg.c | 178 ++++++++++++++++++++++++++++++--- net/wireless/reg.h | 2 + 7 files changed, 194 insertions(+), 41 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index b47cbe9e7a5..1e3824215ac 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1656,8 +1656,12 @@ int ath_attach(u16 devid, struct ath_softc *sc) error = ieee80211_register_hw(hw); - if (!ath9k_is_world_regd(sc->sc_ah)) - regulatory_hint(hw->wiphy, sc->sc_ah->regulatory.alpha2); + if (!ath9k_is_world_regd(sc->sc_ah)) { + error = regulatory_hint(hw->wiphy, + sc->sc_ah->regulatory.alpha2); + if (error) + goto error_attach; + } /* Initialize LED control */ ath_init_leds(sc); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 7579af27edb..da9214e33a5 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -170,10 +170,10 @@ int zd_mac_init_hw(struct ieee80211_hw *hw) goto disable_int; r = zd_reg2alpha2(mac->regdomain, alpha2); - if (!r) - regulatory_hint(hw->wiphy, alpha2); + if (r) + goto disable_int; - r = 0; + r = regulatory_hint(hw->wiphy, alpha2); disable_int: zd_chip_disable_int(chip); out: diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bb9129fc61c..75fa556728c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -404,6 +404,7 @@ enum environment_cap { * country IE * @country_ie_env: lets us know if the AP is telling us we are outdoor, * indoor, or if it doesn't matter + * @list: used to insert into the reg_requests_list linked list */ struct regulatory_request { int wiphy_idx; @@ -412,6 +413,7 @@ struct regulatory_request { bool intersect; u32 country_ie_checksum; enum environment_cap country_ie_env; + struct list_head list; }; struct ieee80211_freq_range { diff --git a/include/net/wireless.h b/include/net/wireless.h index d815aa8b453..1f4707d18ad 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -401,8 +401,15 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, * domain should be in or by providing a completely build regulatory domain. * If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried * for a regulatory domain structure for the respective country. + * + * The wiphy must have been registered to cfg80211 prior to this call. + * For cfg80211 drivers this means you must first use wiphy_register(), + * for mac80211 drivers you must first use ieee80211_register_hw(). + * + * Drivers should check the return value, its possible you can get + * an -ENOMEM. */ -extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2); +extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); /** * regulatory_hint_11d - hints a country IE as a regulatory domain diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e0d3879b885..97f69bed3fe 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1915,34 +1915,24 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) */ mutex_lock(&cfg80211_mutex); if (unlikely(!cfg80211_regdomain)) { - r = -EINPROGRESS; - goto out; + mutex_unlock(&cfg80211_mutex); + return -EINPROGRESS; } + mutex_unlock(&cfg80211_mutex); - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) { - r = -EINVAL; - goto out; - } + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); #ifdef CONFIG_WIRELESS_OLD_REGULATORY /* We ignore world regdom requests with the old regdom setup */ - if (is_world_regdom(data)) { - r = -EINVAL; - goto out; - } + if (is_world_regdom(data)) + return -EINVAL; #endif - r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); - /* - * This means the regulatory domain was already set, however - * we don't want to confuse userspace with a "successful error" - * message so lets just treat it as a success - */ - if (r == -EALREADY) - r = 0; -out: - mutex_unlock(&cfg80211_mutex); + + r = regulatory_hint_user(data); + return r; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index af762be3f0a..0b8c4b86789 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -64,6 +64,9 @@ const struct ieee80211_regdomain *cfg80211_regdomain; * what it thinks should apply for the same country */ static const struct ieee80211_regdomain *country_ie_regdomain; +static LIST_HEAD(reg_requests_list); +static spinlock_t reg_requests_lock; + /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 1, @@ -831,7 +834,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, const struct ieee80211_power_rule *power_rule = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - struct wiphy *request_wiphy; + struct wiphy *request_wiphy = NULL; assert_cfg80211_lock(); @@ -1195,6 +1198,89 @@ new_request: return call_crda(alpha2); } +/* This currently only processes user and driver regulatory hints */ +static int reg_process_hint(struct regulatory_request *reg_request) +{ + int r = 0; + struct wiphy *wiphy = NULL; + + BUG_ON(!reg_request->alpha2); + + mutex_lock(&cfg80211_mutex); + + if (wiphy_idx_valid(reg_request->wiphy_idx)) + wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); + + if (reg_request->initiator == REGDOM_SET_BY_DRIVER && + !wiphy) { + r = -ENODEV; + goto out; + } + + r = __regulatory_hint(wiphy, + reg_request->initiator, + reg_request->alpha2, + reg_request->country_ie_checksum, + reg_request->country_ie_env); + /* This is required so that the orig_* parameters are saved */ + if (r == -EALREADY && wiphy && wiphy->strict_regulatory) + wiphy_update_regulatory(wiphy, reg_request->initiator); +out: + mutex_unlock(&cfg80211_mutex); + + if (r == -EALREADY) + r = 0; + + return r; +} + +static void reg_process_pending_hints(void) + { + struct regulatory_request *reg_request; + int r; + + spin_lock(®_requests_lock); + while (!list_empty(®_requests_list)) { + reg_request = list_first_entry(®_requests_list, + struct regulatory_request, + list); + list_del_init(®_request->list); + spin_unlock(®_requests_lock); + + r = reg_process_hint(reg_request); +#ifdef CONFIG_CFG80211_REG_DEBUG + if (r && (reg_request->initiator == REGDOM_SET_BY_DRIVER || + reg_request->initiator == REGDOM_SET_BY_COUNTRY_IE)) + printk(KERN_ERR "cfg80211: wiphy_idx %d sent a " + "regulatory hint for %c%c but now has " + "gone fishing, ignoring request\n", + reg_request->wiphy_idx, + reg_request->alpha2[0], + reg_request->alpha2[1]); +#endif + kfree(reg_request); + spin_lock(®_requests_lock); + } + spin_unlock(®_requests_lock); +} + +static void reg_todo(struct work_struct *work) +{ + reg_process_pending_hints(); +} + +static DECLARE_WORK(reg_work, reg_todo); + +static void queue_regulatory_request(struct regulatory_request *request) +{ + spin_lock(®_requests_lock); + list_add_tail(&request->list, ®_requests_list); + spin_unlock(®_requests_lock); + + schedule_work(®_work); +} + +/* Core regulatory hint -- happens once during cfg80211_init() */ static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; @@ -1210,23 +1296,56 @@ static int regulatory_hint_core(const char *alpha2) request->alpha2[1] = alpha2[1]; request->initiator = REGDOM_SET_BY_CORE; - last_request = request; + queue_regulatory_request(request); - return call_crda(alpha2); + return 0; } -void regulatory_hint(struct wiphy *wiphy, const char *alpha2) +/* User hints */ +int regulatory_hint_user(const char *alpha2) { - int r; + struct regulatory_request *request; + BUG_ON(!alpha2); - mutex_lock(&cfg80211_mutex); - r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, - alpha2, 0, ENVIRON_ANY); - /* This is required so that the orig_* parameters are saved */ - if (r == -EALREADY && wiphy->strict_regulatory) - wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER); - mutex_unlock(&cfg80211_mutex); + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wiphy_idx = WIPHY_IDX_STALE; + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = REGDOM_SET_BY_USER, + + queue_regulatory_request(request); + + return 0; +} + +/* Driver hints */ +int regulatory_hint(struct wiphy *wiphy, const char *alpha2) +{ + struct regulatory_request *request; + + BUG_ON(!alpha2); + BUG_ON(!wiphy); + + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wiphy_idx = get_wiphy_idx(wiphy); + + /* Must have registered wiphy first */ + BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); + + request->alpha2[0] = alpha2[0]; + request->alpha2[1] = alpha2[1]; + request->initiator = REGDOM_SET_BY_DRIVER; + + queue_regulatory_request(request); + + return 0; } EXPORT_SYMBOL(regulatory_hint); @@ -1260,6 +1379,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, char alpha2[2]; u32 checksum = 0; enum environment_cap env = ENVIRON_ANY; + struct regulatory_request *request; mutex_lock(&cfg80211_mutex); @@ -1343,14 +1463,26 @@ void regulatory_hint_11d(struct wiphy *wiphy, if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum))) goto free_rd_out; + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + goto free_rd_out; + /* We keep this around for when CRDA comes back with a response so * we can intersect with that */ country_ie_regdomain = rd; - __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE, - country_ie_regdomain->alpha2, checksum, env); + request->wiphy_idx = get_wiphy_idx(wiphy); + request->alpha2[0] = rd->alpha2[0]; + request->alpha2[1] = rd->alpha2[1]; + request->initiator = REGDOM_SET_BY_COUNTRY_IE; + request->country_ie_checksum = checksum; + request->country_ie_env = env; + + mutex_unlock(&cfg80211_mutex); - goto out; + queue_regulatory_request(request); + + return; free_rd_out: kfree(rd); @@ -1661,6 +1793,8 @@ int regulatory_init(void) if (IS_ERR(reg_pdev)) return PTR_ERR(reg_pdev); + spin_lock_init(®_requests_lock); + #ifdef CONFIG_WIRELESS_OLD_REGULATORY cfg80211_regdomain = static_regdom(ieee80211_regdom); @@ -1700,6 +1834,10 @@ int regulatory_init(void) void regulatory_exit(void) { + struct regulatory_request *reg_request, *tmp; + + cancel_work_sync(®_work); + mutex_lock(&cfg80211_mutex); reset_regdomains(); @@ -1711,5 +1849,15 @@ void regulatory_exit(void) platform_device_unregister(reg_pdev); + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) { + list_for_each_entry_safe(reg_request, tmp, + ®_requests_list, list) { + list_del(®_request->list); + kfree(reg_request); + } + } + spin_unlock(®_requests_lock); + mutex_unlock(&cfg80211_mutex); } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index fe8c83f34fb..4730def5a69 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -6,6 +6,8 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); bool reg_is_valid_request(const char *alpha2); +int regulatory_hint_user(const char *alpha2); + void reg_device_remove(struct wiphy *wiphy); int regulatory_init(void); -- cgit v1.2.3-70-g09d2 From e38f8a7a8bebbab9d97f204e2cf05ef58b048a1d Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Feb 2009 00:20:39 -0500 Subject: cfg80211: Add AP beacon regulatory hints When devices are world roaming they cannot beacon or do active scan on 5 GHz or on channels 12, 13 and 14 on the 2 GHz band. Although we have a good regulatory API some cards may _always_ world roam, this is also true when a system does not have CRDA present. Devices doing world roaming can still passive scan, if they find a beacon from an AP on one of the world roaming frequencies we make the assumption we can do the same and we also remove the passive scan requirement. This adds support for providing beacon regulatory hints based on scans. This works for devices that do either hardware or software scanning. If a channel has not yet been marked as having had a beacon present on it we queue the beacon hint processing into the workqueue. All wireless devices will benefit from beacon regulatory hints from any wireless device on a system including new devices connected to the system at a later time. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/wireless.h | 5 +- net/wireless/core.c | 5 +- net/wireless/reg.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++- net/wireless/reg.h | 21 +++++ net/wireless/scan.c | 3 + 5 files changed, 254 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/wireless.h b/include/net/wireless.h index 1f4707d18ad..64a76208580 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -69,6 +69,9 @@ enum ieee80211_channel_flags { * @band: band this channel belongs to. * @max_antenna_gain: maximum antenna gain in dBi * @max_power: maximum transmission power (in dBm) + * @beacon_found: helper to regulatory code to indicate when a beacon + * has been found on this channel. Use regulatory_hint_found_beacon() + * to enable this, this is is useful only on 5 GHz band. * @orig_mag: internal use * @orig_mpwr: internal use */ @@ -80,6 +83,7 @@ struct ieee80211_channel { u32 flags; int max_antenna_gain; int max_power; + bool beacon_found; u32 orig_flags; int orig_mag, orig_mpwr; }; @@ -425,7 +429,6 @@ extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); extern void regulatory_hint_11d(struct wiphy *wiphy, u8 *country_ie, u8 country_ie_len); - /** * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain * @wiphy: the wireless device we want to process the regulatory domain on diff --git a/net/wireless/core.c b/net/wireless/core.c index b1a354b7fc0..dd7f222919f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -32,8 +32,9 @@ MODULE_DESCRIPTION("wireless configuration support"); LIST_HEAD(cfg80211_drv_list); /* - * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, and - * the last reguluatory request receipt in regd.c + * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, + * country_ie_regdomain, the reg_beacon_list and the the last regulatory + * request receipt (last_request). */ DEFINE_MUTEX(cfg80211_mutex); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index da2a8aca428..e5e432d6af3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -68,9 +68,22 @@ const struct ieee80211_regdomain *cfg80211_regdomain; */ static const struct ieee80211_regdomain *country_ie_regdomain; +/* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; +/* Used to queue up beacon hints for review */ +static LIST_HEAD(reg_pending_beacons); +static spinlock_t reg_pending_beacons_lock; + +/* Used to keep track of processed beacon hints */ +static LIST_HEAD(reg_beacon_list); + +struct reg_beacon { + struct list_head list; + struct ieee80211_channel chan; +}; + /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 3, @@ -1011,16 +1024,120 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby) wiphy_update_regulatory(&drv->wiphy, setby); } +static void handle_reg_beacon(struct wiphy *wiphy, + unsigned int chan_idx, + struct reg_beacon *reg_beacon) +{ +#ifdef CONFIG_CFG80211_REG_DEBUG +#define REG_DEBUG_BEACON_FLAG(desc) \ + printk(KERN_DEBUG "cfg80211: Enabling " desc " on " \ + "frequency: %d MHz (Ch %d) on %s\n", \ + reg_beacon->chan.center_freq, \ + ieee80211_frequency_to_channel(reg_beacon->chan.center_freq), \ + wiphy_name(wiphy)); +#else +#define REG_DEBUG_BEACON_FLAG(desc) do {} while (0) +#endif + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + + assert_cfg80211_lock(); + + sband = wiphy->bands[reg_beacon->chan.band]; + chan = &sband->channels[chan_idx]; + + if (likely(chan->center_freq != reg_beacon->chan.center_freq)) + return; + + if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + REG_DEBUG_BEACON_FLAG("active scanning"); + } + + if (chan->flags & IEEE80211_CHAN_NO_IBSS) { + chan->flags &= ~IEEE80211_CHAN_NO_IBSS; + REG_DEBUG_BEACON_FLAG("beaconing"); + } + + chan->beacon_found = true; +#undef REG_DEBUG_BEACON_FLAG +} + +/* + * Called when a scan on a wiphy finds a beacon on + * new channel + */ +static void wiphy_update_new_beacon(struct wiphy *wiphy, + struct reg_beacon *reg_beacon) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + + assert_cfg80211_lock(); + + if (!wiphy->bands[reg_beacon->chan.band]) + return; + + sband = wiphy->bands[reg_beacon->chan.band]; + + for (i = 0; i < sband->n_channels; i++) + handle_reg_beacon(wiphy, i, reg_beacon); +} + +/* + * Called upon reg changes or a new wiphy is added + */ +static void wiphy_update_beacon_reg(struct wiphy *wiphy) +{ + unsigned int i; + struct ieee80211_supported_band *sband; + struct reg_beacon *reg_beacon; + + assert_cfg80211_lock(); + + if (list_empty(®_beacon_list)) + return; + + list_for_each_entry(reg_beacon, ®_beacon_list, list) { + if (!wiphy->bands[reg_beacon->chan.band]) + continue; + sband = wiphy->bands[reg_beacon->chan.band]; + for (i = 0; i < sband->n_channels; i++) + handle_reg_beacon(wiphy, i, reg_beacon); + } +} + +static bool reg_is_world_roaming(struct wiphy *wiphy) +{ + if (is_world_regdom(cfg80211_regdomain->alpha2) || + (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) + return true; + if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && + wiphy->custom_regulatory) + return true; + return false; +} + +/* Reap the advantages of previously found beacons */ +static void reg_process_beacons(struct wiphy *wiphy) +{ + if (!reg_is_world_roaming(wiphy)) + return; + wiphy_update_beacon_reg(wiphy); +} + void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) { enum ieee80211_band band; if (ignore_reg_update(wiphy, setby)) - return; + goto out; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) handle_band(wiphy, band); } +out: + reg_process_beacons(wiphy); if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } @@ -1314,6 +1431,7 @@ out: return r; } +/* Processes regulatory hints, this is all the REGDOM_SET_BY_* */ static void reg_process_pending_hints(void) { struct regulatory_request *reg_request; @@ -1344,9 +1462,44 @@ static void reg_process_pending_hints(void) spin_unlock(®_requests_lock); } +/* Processes beacon hints -- this has nothing to do with country IEs */ +static void reg_process_pending_beacon_hints(void) +{ + struct cfg80211_registered_device *drv; + struct reg_beacon *pending_beacon, *tmp; + + mutex_lock(&cfg80211_mutex); + + /* This goes through the _pending_ beacon list */ + spin_lock_bh(®_pending_beacons_lock); + + if (list_empty(®_pending_beacons)) { + spin_unlock_bh(®_pending_beacons_lock); + goto out; + } + + list_for_each_entry_safe(pending_beacon, tmp, + ®_pending_beacons, list) { + + list_del_init(&pending_beacon->list); + + /* Applies the beacon hint to current wiphys */ + list_for_each_entry(drv, &cfg80211_drv_list, list) + wiphy_update_new_beacon(&drv->wiphy, pending_beacon); + + /* Remembers the beacon hint for new wiphys or reg changes */ + list_add_tail(&pending_beacon->list, ®_beacon_list); + } + + spin_unlock_bh(®_pending_beacons_lock); +out: + mutex_unlock(&cfg80211_mutex); +} + static void reg_todo(struct work_struct *work) { reg_process_pending_hints(); + reg_process_pending_beacon_hints(); } static DECLARE_WORK(reg_work, reg_todo); @@ -1587,6 +1740,55 @@ out: } EXPORT_SYMBOL(regulatory_hint_11d); +static bool freq_is_chan_12_13_14(u16 freq) +{ + if (freq == ieee80211_channel_to_frequency(12) || + freq == ieee80211_channel_to_frequency(13) || + freq == ieee80211_channel_to_frequency(14)) + return true; + return false; +} + +int regulatory_hint_found_beacon(struct wiphy *wiphy, + struct ieee80211_channel *beacon_chan, + gfp_t gfp) +{ + struct reg_beacon *reg_beacon; + + if (likely((beacon_chan->beacon_found || + (beacon_chan->flags & IEEE80211_CHAN_RADAR) || + (beacon_chan->band == IEEE80211_BAND_2GHZ && + !freq_is_chan_12_13_14(beacon_chan->center_freq))))) + return 0; + + reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); + if (!reg_beacon) + return -ENOMEM; + +#ifdef CONFIG_CFG80211_REG_DEBUG + printk(KERN_DEBUG "cfg80211: Found new beacon on " + "frequency: %d MHz (Ch %d) on %s\n", + beacon_chan->center_freq, + ieee80211_frequency_to_channel(beacon_chan->center_freq), + wiphy_name(wiphy)); +#endif + memcpy(®_beacon->chan, beacon_chan, + sizeof(struct ieee80211_channel)); + + + /* + * Since we can be called from BH or and non-BH context + * we must use spin_lock_bh() + */ + spin_lock_bh(®_pending_beacons_lock); + list_add_tail(®_beacon->list, ®_pending_beacons); + spin_unlock_bh(®_pending_beacons_lock); + + schedule_work(®_work); + + return 0; +} + static void print_rd_rules(const struct ieee80211_regdomain *rd) { unsigned int i; @@ -1908,6 +2110,7 @@ int regulatory_init(void) return PTR_ERR(reg_pdev); spin_lock_init(®_requests_lock); + spin_lock_init(®_pending_beacons_lock); #ifdef CONFIG_WIRELESS_OLD_REGULATORY cfg80211_regdomain = static_regdom(ieee80211_regdom); @@ -1951,6 +2154,7 @@ int regulatory_init(void) void regulatory_exit(void) { struct regulatory_request *reg_request, *tmp; + struct reg_beacon *reg_beacon, *btmp; cancel_work_sync(®_work); @@ -1965,6 +2169,24 @@ void regulatory_exit(void) platform_device_unregister(reg_pdev); + spin_lock_bh(®_pending_beacons_lock); + if (!list_empty(®_pending_beacons)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + spin_unlock_bh(®_pending_beacons_lock); + + if (!list_empty(®_beacon_list)) { + list_for_each_entry_safe(reg_beacon, btmp, + ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); + } + } + spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) { list_for_each_entry_safe(reg_request, tmp, diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4730def5a69..65bfd0558ce 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -38,4 +38,25 @@ extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, const char *alpha2, u32 country_ie_checksum, enum environment_cap country_ie_env); +/** + * regulatory_hint_found_beacon - hints a beacon was found on a channel + * @wiphy: the wireless device where the beacon was found on + * @beacon_chan: the channel on which the beacon was found on + * @gfp: context flags + * + * This informs the wireless core that a beacon from an AP was found on + * the channel provided. This allows the wireless core to make educated + * guesses on regulatory to help with world roaming. This is only used for + * world roaming -- when we do not know our current location. This is + * only useful on channels 12, 13 and 14 on the 2 GHz band as channels + * 1-11 are already enabled by the world regulatory domain; and on + * non-radar 5 GHz channels. + * + * Drivers do not need to call this, cfg80211 will do it for after a scan + * on a newly found BSS. + */ +int regulatory_hint_found_beacon(struct wiphy *wiphy, + struct ieee80211_channel *beacon_chan, + gfp_t gfp); + #endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 60600657b65..280dbcd02c1 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -430,6 +430,9 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, if (!res) return NULL; + if (res->pub.capability & WLAN_CAPABILITY_ESS) + regulatory_hint_found_beacon(wiphy, channel, gfp); + /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } -- cgit v1.2.3-70-g09d2 From 758ce5c8d11d6fc57fe5f1dbc237aa8ff6386eac Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 28 Feb 2009 04:44:37 +0000 Subject: tcp: add helper for AI algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems that implementation in yeah was inconsistent to what other did as it would increase cwnd one ack earlier than the others do. Size benefits: bictcp_cong_avoid | -36 tcp_cong_avoid_ai | +52 bictcp_cong_avoid | -34 tcp_scalable_cong_avoid | -36 tcp_veno_cong_avoid | -12 tcp_yeah_cong_avoid | -38 = -104 bytes total Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_bic.c | 11 +---------- net/ipv4/tcp_cong.c | 21 ++++++++++++++------- net/ipv4/tcp_cubic.c | 11 +---------- net/ipv4/tcp_scalable.c | 10 ++-------- net/ipv4/tcp_veno.c | 7 +------ net/ipv4/tcp_yeah.c | 9 +-------- 7 files changed, 21 insertions(+), 49 deletions(-) (limited to 'include/net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 218235de896..0366a559afe 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -685,6 +685,7 @@ extern void tcp_get_allowed_congestion_control(char *buf, size_t len); extern int tcp_set_allowed_congestion_control(char *allowed); extern int tcp_set_congestion_control(struct sock *sk, const char *name); extern void tcp_slow_start(struct tcp_sock *tp); +extern void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w); extern struct tcp_congestion_ops tcp_init_congestion_ops; extern u32 tcp_reno_ssthresh(struct sock *sk); diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 7eb7636db0d..3b53fd1af23 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -149,16 +149,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) tcp_slow_start(tp); else { bictcp_update(ca, tp->snd_cwnd); - - /* In dangerous area, increase slowly. - * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd - */ - if (tp->snd_cwnd_cnt >= ca->cnt) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } else - tp->snd_cwnd_cnt++; + tcp_cong_avoid_ai(tp, ca->cnt); } } diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 4ec5b4e97c4..e92beb9e55e 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -336,6 +336,19 @@ void tcp_slow_start(struct tcp_sock *tp) } EXPORT_SYMBOL_GPL(tcp_slow_start); +/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd (or alternative w) */ +void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w) +{ + if (tp->snd_cwnd_cnt >= w) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } else { + tp->snd_cwnd_cnt++; + } +} +EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai); + /* * TCP Reno congestion control * This is special case used for fallback as well. @@ -365,13 +378,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) tp->snd_cwnd++; } } else { - /* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd */ - if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } else - tp->snd_cwnd_cnt++; + tcp_cong_avoid_ai(tp, tp->snd_cwnd); } } EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid); diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index ee467ec40c4..71d5f2f29fa 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -294,16 +294,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) tcp_slow_start(tp); } else { bictcp_update(ca, tp->snd_cwnd); - - /* In dangerous area, increase slowly. - * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd - */ - if (tp->snd_cwnd_cnt >= ca->cnt) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } else - tp->snd_cwnd_cnt++; + tcp_cong_avoid_ai(tp, ca->cnt); } } diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 4660b088a8c..a76513779e2 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -24,14 +24,8 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) if (tp->snd_cwnd <= tp->snd_ssthresh) tcp_slow_start(tp); - else { - tp->snd_cwnd_cnt++; - if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){ - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } - } + else + tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)); } static u32 tcp_scalable_ssthresh(struct sock *sk) diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index d08b2e855c2..e9bbff74648 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -159,12 +159,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* In the "non-congestive state", increase cwnd * every rtt. */ - if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } else - tp->snd_cwnd_cnt++; + tcp_cong_avoid_ai(tp, tp->snd_cwnd); } else { /* In the "congestive state", increase cwnd * every other rtt. diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 9ec843a9bbb..66b6821b984 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -94,14 +94,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) } else { /* Reno */ - - if (tp->snd_cwnd_cnt < tp->snd_cwnd) - tp->snd_cwnd_cnt++; - - if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } + tcp_cong_avoid_ai(tp, tp->snd_cwnd); } /* The key players are v_vegas.beg_snd_una and v_beg_snd_nxt. -- cgit v1.2.3-70-g09d2 From cabeccbd172cc305f4383f5a4808ae254745275f Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 28 Feb 2009 04:44:38 +0000 Subject: tcp: kill eff_sacks "cache", the sole user can calculate itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fixes insignificant bug that would cause sending of stale SACK block (would occur in some corner cases). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - net/ipv4/tcp_input.c | 15 ++------------- net/ipv4/tcp_minisocks.c | 3 +-- net/ipv4/tcp_output.c | 12 ++++++------ 5 files changed, 9 insertions(+), 23 deletions(-) (limited to 'include/net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 0cd99e6baca..4b86ad71e05 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -218,7 +218,6 @@ struct tcp_options_received { snd_wscale : 4, /* Window scaling received from sender */ rcv_wscale : 4; /* Window scaling to send to receiver */ /* SACKs data */ - u8 eff_sacks; /* Size of SACK array to send with next packet */ u8 num_sacks; /* Number of SACK blocks */ u16 user_mss; /* mss requested by user in ioctl */ u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 0366a559afe..055e4946d4c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -926,7 +926,6 @@ extern void tcp_done(struct sock *sk); static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) { rx_opt->dsack = 0; - rx_opt->eff_sacks = 0; rx_opt->num_sacks = 0; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 03f5ede8722..e4442a293eb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4099,7 +4099,6 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq) tp->rx_opt.dsack = 1; tp->duplicate_sack[0].start_seq = seq; tp->duplicate_sack[0].end_seq = end_seq; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + 1; } } @@ -4154,8 +4153,6 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp) * Decrease num_sacks. */ tp->rx_opt.num_sacks--; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + - tp->rx_opt.dsack; for (i = this_sack; i < tp->rx_opt.num_sacks; i++) sp[i] = sp[i + 1]; continue; @@ -4218,7 +4215,6 @@ new_sack: sp->start_seq = seq; sp->end_seq = end_seq; tp->rx_opt.num_sacks++; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; } /* RCV.NXT advances, some SACKs should be eaten. */ @@ -4232,7 +4228,6 @@ static void tcp_sack_remove(struct tcp_sock *tp) /* Empty ofo queue, hence, all the SACKs are eaten. Clear. */ if (skb_queue_empty(&tp->out_of_order_queue)) { tp->rx_opt.num_sacks = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.dsack; return; } @@ -4253,11 +4248,8 @@ static void tcp_sack_remove(struct tcp_sock *tp) this_sack++; sp++; } - if (num_sacks != tp->rx_opt.num_sacks) { + if (num_sacks != tp->rx_opt.num_sacks) tp->rx_opt.num_sacks = num_sacks; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + - tp->rx_opt.dsack; - } } /* This one checks to see if we can put data from the @@ -4333,10 +4325,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) TCP_ECN_accept_cwr(tp, skb); - if (tp->rx_opt.dsack) { + if (tp->rx_opt.dsack) tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; - } /* Queue data for delivery to the user. * Packets in sequence go to the receive queue. @@ -4456,7 +4446,6 @@ drop: if (tcp_is_sack(tp)) { tp->rx_opt.num_sacks = 1; tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = 1; tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f67effbb102..bb3d8b35f19 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -434,9 +434,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rx_opt.saw_tstamp = 0; newtp->rx_opt.dsack = 0; - newtp->rx_opt.eff_sacks = 0; - newtp->rx_opt.num_sacks = 0; + newtp->urg_data = 0; if (sock_flag(newsk, SOCK_KEEPOPEN)) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 61445b57610..1555bb73b63 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -441,10 +441,8 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, *ptr++ = htonl(sp[this_sack].end_seq); } - if (tp->rx_opt.dsack) { + if (tp->rx_opt.dsack) tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; - } } } @@ -550,6 +548,7 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; struct tcp_sock *tp = tcp_sk(sk); unsigned size = 0; + unsigned int eff_sacks; #ifdef CONFIG_TCP_MD5SIG *md5 = tp->af_specific->md5_lookup(sk, sk); @@ -568,10 +567,11 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, size += TCPOLEN_TSTAMP_ALIGNED; } - if (unlikely(tp->rx_opt.eff_sacks)) { + eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; + if (unlikely(eff_sacks)) { const unsigned remaining = MAX_TCP_OPTION_SPACE - size; opts->num_sack_blocks = - min_t(unsigned, tp->rx_opt.eff_sacks, + min_t(unsigned, eff_sacks, (remaining - TCPOLEN_SACK_BASE_ALIGNED) / TCPOLEN_SACK_PERBLOCK); size += TCPOLEN_SACK_BASE_ALIGNED + @@ -1418,7 +1418,7 @@ static int tcp_mtu_probe(struct sock *sk) icsk->icsk_mtup.probe_size || inet_csk(sk)->icsk_ca_state != TCP_CA_Open || tp->snd_cwnd < 11 || - tp->rx_opt.eff_sacks) + tp->rx_opt.num_sacks || tp->rx_opt.dsack) return -1; /* Very simple search strategy: just double the MSS. */ -- cgit v1.2.3-70-g09d2 From ee7537b63a28b42b22e48842dfeedc66d96b71f1 Mon Sep 17 00:00:00 2001 From: Hantzis Fotis Date: Mon, 2 Mar 2009 22:42:02 -0800 Subject: tcp: tcp_init_wl / tcp_update_wl argument cleanup The above functions from include/net/tcp.h have been defined with an argument that they never use. The argument is 'u32 ack' which is never used inside the function body, and thus it can be removed. The rest of the patch involves the necessary changes to the function callers of the above two functions. Signed-off-by: Hantzis Fotis Signed-off-by: David S. Miller --- include/net/tcp.h | 4 ++-- net/ipv4/tcp_input.c | 9 ++++----- net/ipv4/tcp_minisocks.c | 2 +- net/ipv4/tcp_output.c | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 055e4946d4c..d74ac301e6b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -825,12 +825,12 @@ static inline void tcp_push_pending_frames(struct sock *sk) __tcp_push_pending_frames(sk, tcp_current_mss(sk, 1), tp->nonagle); } -static inline void tcp_init_wl(struct tcp_sock *tp, u32 ack, u32 seq) +static inline void tcp_init_wl(struct tcp_sock *tp, u32 seq) { tp->snd_wl1 = seq; } -static inline void tcp_update_wl(struct tcp_sock *tp, u32 ack, u32 seq) +static inline void tcp_update_wl(struct tcp_sock *tp, u32 seq) { tp->snd_wl1 = seq; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e4442a293eb..5ecd7aa2597 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3416,7 +3416,7 @@ static int tcp_ack_update_window(struct sock *sk, struct sk_buff *skb, u32 ack, if (tcp_may_update_window(tp, ack, ack_seq, nwin)) { flag |= FLAG_WIN_UPDATE; - tcp_update_wl(tp, ack, ack_seq); + tcp_update_wl(tp, ack_seq); if (tp->snd_wnd != nwin) { tp->snd_wnd = nwin; @@ -3621,7 +3621,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) * No more checks are required. * Note, we use the fact that SND.UNA>=SND.WL2. */ - tcp_update_wl(tp, ack, ack_seq); + tcp_update_wl(tp, ack_seq); tp->snd_una = ack; flag |= FLAG_WIN_UPDATE; @@ -5418,7 +5418,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * never scaled. */ tp->snd_wnd = ntohs(th->window); - tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq); + tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); if (!tp->rx_opt.wscale_ok) { tp->rx_opt.snd_wscale = tp->rx_opt.rcv_wscale = 0; @@ -5679,8 +5679,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_una = TCP_SKB_CB(skb)->ack_seq; tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; - tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, - TCP_SKB_CB(skb)->seq); + tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); /* tcp_ack considers this ACK as duplicate * and does not calculate rtt. diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index bb3d8b35f19..4b0df3e6b60 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -399,7 +399,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, tcp_prequeue_init(newtp); - tcp_init_wl(newtp, treq->snt_isn, treq->rcv_isn); + tcp_init_wl(newtp, treq->rcv_isn); newtp->srtt = 0; newtp->mdev = TCP_TIMEOUT_INIT; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 920c57b90de..eb285befdf3 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2337,7 +2337,7 @@ static void tcp_connect_init(struct sock *sk) sk->sk_err = 0; sock_reset_flag(sk, SOCK_DONE); tp->snd_wnd = 0; - tcp_init_wl(tp, tp->write_seq, 0); + tcp_init_wl(tp, 0); tp->snd_una = tp->write_seq; tp->snd_sml = tp->write_seq; tp->snd_up = tp->write_seq; -- cgit v1.2.3-70-g09d2 From 7e99013a5043cacd375375c3efad35b57c3afdba Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Mon, 2 Mar 2009 09:46:14 +0000 Subject: sctp: Fix broken RTO-doubling for data retransmits Commit faee47cdbfe8d74a1573c2f81ea6dbb08d735be6 (sctp: Fix the RTO-doubling on idle-link heartbeats) broke the RTO doubling for data retransmits. If the heartbeat was sent before the data T3-rtx time, the the RTO will not double upon the T3-rtx expiration. Distingish between the operations by passing an argument to the function. Additionally, Wei Youngjun pointed out that our treatment of requested HEARTBEATS and timer HEARTBEATS is the same wrt resetting congestion window. That needs to be separated, since user requested HEARTBEATS should not treat the link as idle. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/command.h | 3 ++- net/sctp/sm_sideeffect.c | 32 +++++++++++++------------------- net/sctp/sm_statefuns.c | 6 ++++-- 3 files changed, 19 insertions(+), 22 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 88988ab03d7..3b966802e05 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -77,7 +77,8 @@ typedef enum { SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */ SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */ SCTP_CMD_HB_TIMERS_STOP, /* Stop the heartbeat timers. */ - SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */ + SCTP_CMD_TRANSPORT_HB_SENT, /* Reset the status of a transport. */ + SCTP_CMD_TRANSPORT_IDLE, /* Do manipulations on idle transport */ SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */ SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */ SCTP_CMD_REPORT_BAD_TAG, /* Verification tags didn't match. */ diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 0146cfb1f18..5385150df29 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -434,7 +434,8 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { * */ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, - struct sctp_transport *transport) + struct sctp_transport *transport, + int is_hb) { /* The check for association's overall error counter exceeding the * threshold is done in the state function. @@ -466,7 +467,7 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, * The first unacknowleged HB triggers it. We do this with a flag * that indicates that we have an outstanding HB. */ - if (transport->hb_sent) { + if (!is_hb || transport->hb_sent) { transport->last_rto = transport->rto; transport->rto = min((transport->rto * 2), transport->asoc->rto_max); } @@ -657,20 +658,6 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, sctp_transport_hold(t); } -/* Helper function to do a transport reset at the expiry of the hearbeat - * timer. - */ -static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, - struct sctp_association *asoc, - struct sctp_transport *t) -{ - sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); - - /* Mark one strike against a transport. */ - sctp_do_8_2_transport_strike(asoc, t); - - t->hb_sent = 1; -} /* Helper function to process the process SACK command. */ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, @@ -1459,12 +1446,19 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_STRIKE: /* Mark one strike against a transport. */ - sctp_do_8_2_transport_strike(asoc, cmd->obj.transport); + sctp_do_8_2_transport_strike(asoc, cmd->obj.transport, + 0); + break; + + case SCTP_CMD_TRANSPORT_IDLE: + t = cmd->obj.transport; + sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); break; - case SCTP_CMD_TRANSPORT_RESET: + case SCTP_CMD_TRANSPORT_HB_SENT: t = cmd->obj.transport; - sctp_cmd_transport_reset(commands, asoc, t); + sctp_do_8_2_transport_strike(asoc, t, 1); + t->hb_sent = 1; break; case SCTP_CMD_TRANSPORT_ON: diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3a0cd075914..a907bab0963 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -988,7 +988,9 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, /* Set transport error counter and association error counter * when sending heartbeat. */ - sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_IDLE, + SCTP_TRANSPORT(transport)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_HB_SENT, SCTP_TRANSPORT(transport)); } sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE, @@ -4967,7 +4969,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( * to that address and not acknowledged within one RTO. * */ - sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_HB_SENT, SCTP_TRANSPORT(arg)); return SCTP_DISPOSITION_CONSUME; } -- cgit v1.2.3-70-g09d2 From c887e6d2d9aee56ee7c9f2af4cec3a5efdcc4c72 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 14 Mar 2009 14:23:03 +0000 Subject: tcp: consolidate paws check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wow, it was quite tricky to merge that stream of negations but I think I finally got it right: check & replace_ts_recent: (s32)(rcv_tsval - ts_recent) >= 0 => 0 (s32)(ts_recent - rcv_tsval) <= 0 => 0 discard: (s32)(ts_recent - rcv_tsval) > TCP_PAWS_WINDOW => 1 (s32)(ts_recent - rcv_tsval) <= TCP_PAWS_WINDOW => 0 I toggled the return values of tcp_paws_check around since the old encoding added yet-another negation making tracking of truth-values really complicated. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/net/tcp.h | 18 ++++++++++++++---- net/ipv4/tcp_input.c | 11 +++++------ net/ipv4/tcp_minisocks.c | 4 ++-- 3 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/tcp.h b/include/net/tcp.h index d74ac301e6b..255ca35bea0 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -997,11 +997,21 @@ static inline int tcp_fin_time(const struct sock *sk) return fin_timeout; } -static inline int tcp_paws_check(const struct tcp_options_received *rx_opt, int rst) +static inline int tcp_paws_check(const struct tcp_options_received *rx_opt, + int paws_win) { - if ((s32)(rx_opt->rcv_tsval - rx_opt->ts_recent) >= 0) - return 0; - if (get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS) + if ((s32)(rx_opt->ts_recent - rx_opt->rcv_tsval) <= paws_win) + return 1; + if (unlikely(get_seconds() >= rx_opt->ts_recent_stamp + TCP_PAWS_24DAYS)) + return 1; + + return 0; +} + +static inline int tcp_paws_reject(const struct tcp_options_received *rx_opt, + int rst) +{ + if (tcp_paws_check(rx_opt, 0)) return 0; /* RST segments are not recommended to carry timestamp, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index f527a16a7b3..b7d02c5dd6d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3883,8 +3883,7 @@ static inline void tcp_replace_ts_recent(struct tcp_sock *tp, u32 seq) * Not only, also it occurs for expired timestamps. */ - if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) >= 0 || - get_seconds() >= tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS) + if (tcp_paws_check(&tp->rx_opt, 0)) tcp_store_ts_recent(tp); } } @@ -3936,9 +3935,9 @@ static inline int tcp_paws_discard(const struct sock *sk, const struct sk_buff *skb) { const struct tcp_sock *tp = tcp_sk(sk); - return ((s32)(tp->rx_opt.ts_recent - tp->rx_opt.rcv_tsval) > TCP_PAWS_WINDOW && - get_seconds() < tp->rx_opt.ts_recent_stamp + TCP_PAWS_24DAYS && - !tcp_disordered_ack(sk, skb)); + + return !tcp_paws_check(&tp->rx_opt, TCP_PAWS_WINDOW) && + !tcp_disordered_ack(sk, skb); } /* Check segment sequence number for validity. @@ -5513,7 +5512,7 @@ discard: /* PAWS check. */ if (tp->rx_opt.ts_recent_stamp && tp->rx_opt.saw_tstamp && - tcp_paws_check(&tp->rx_opt, 0)) + tcp_paws_reject(&tp->rx_opt, 0)) goto discard_and_undo; if (th->syn) { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 4b0df3e6b60..43bbba7926e 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -107,7 +107,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = tcptw->tw_ts_recent; tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; - paws_reject = tcp_paws_check(&tmp_opt, th->rst); + paws_reject = tcp_paws_reject(&tmp_opt, th->rst); } } @@ -511,7 +511,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * from another data. */ tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<retrans); - paws_reject = tcp_paws_check(&tmp_opt, th->rst); + paws_reject = tcp_paws_reject(&tmp_opt, th->rst); } } -- cgit v1.2.3-70-g09d2 From 0c54b85f2828128274f319a1eb3ce7f604fe2a53 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 14 Mar 2009 14:23:05 +0000 Subject: tcp: simplify tcp_current_mss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's very little need for most of the callsites to get tp->xmit_goal_size updated. That will cost us divide as is, so slice the function in two. Also, the only users of the tp->xmit_goal_size are directly behind tcp_current_mss(), so there's no need to store that variable into tcp_sock at all! The drop of xmit_goal_size currently leaves 16-bit hole and some reorganization would again be necessary to change that (but I'm aiming to fill that hole with u16 xmit_goal_size_segs to cache the results of the remaining divide to get that tso on regression). Bring xmit_goal_size parts into tcp.c Signed-off-by: Ilpo Järvinen Cc: Evgeniy Polyakov Cc: Ingo Molnar Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 13 +++++++++++-- net/ipv4/tcp.c | 43 +++++++++++++++++++++++++++++++++++-------- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 41 +++++++---------------------------------- 5 files changed, 54 insertions(+), 46 deletions(-) (limited to 'include/net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4b86ad71e05..ad2021ccc55 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -248,7 +248,6 @@ struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; u16 tcp_header_len; /* Bytes of tcp header to send */ - u16 xmit_size_goal; /* Goal for segmenting output packets */ /* * Header prediction flags diff --git a/include/net/tcp.h b/include/net/tcp.h index 255ca35bea0..e54c76d7549 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -481,7 +481,16 @@ static inline void tcp_clear_xmit_timers(struct sock *sk) } extern unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu); -extern unsigned int tcp_current_mss(struct sock *sk, int large); +extern unsigned int tcp_current_mss(struct sock *sk); + +/* Bound MSS / TSO packet size with the half of the window */ +static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) +{ + if (tp->max_window && pktsize > (tp->max_window >> 1)) + return max(tp->max_window >> 1, 68U - tp->tcp_header_len); + else + return pktsize; +} /* tcp.c */ extern void tcp_get_info(struct sock *, struct tcp_info *); @@ -822,7 +831,7 @@ static inline void tcp_push_pending_frames(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - __tcp_push_pending_frames(sk, tcp_current_mss(sk, 1), tp->nonagle); + __tcp_push_pending_frames(sk, tcp_current_mss(sk), tp->nonagle); } static inline void tcp_init_wl(struct tcp_sock *tp, u32 seq) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d3f9beee74c..886596ff0aa 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -661,6 +661,37 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp) return NULL; } +static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, + int large_allowed) +{ + struct tcp_sock *tp = tcp_sk(sk); + u32 xmit_size_goal; + + xmit_size_goal = mss_now; + + if (large_allowed && sk_can_gso(sk)) { + xmit_size_goal = ((sk->sk_gso_max_size - 1) - + inet_csk(sk)->icsk_af_ops->net_header_len - + inet_csk(sk)->icsk_ext_hdr_len - + tp->tcp_header_len); + + xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); + xmit_size_goal -= (xmit_size_goal % mss_now); + } + + return xmit_size_goal; +} + +static int tcp_send_mss(struct sock *sk, int *size_goal, int flags) +{ + int mss_now; + + mss_now = tcp_current_mss(sk); + *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); + + return mss_now; +} + static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags) { @@ -677,8 +708,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); copied = 0; err = -EPIPE; @@ -761,8 +791,7 @@ wait_for_memory: if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); } out: @@ -844,8 +873,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, /* This should be in poll */ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); /* Ok commence sending. */ iovlen = msg->msg_iovlen; @@ -1007,8 +1035,7 @@ wait_for_memory: if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 311c30f73ee..fae78e3eccc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2864,7 +2864,7 @@ void tcp_simple_retransmit(struct sock *sk) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - unsigned int mss = tcp_current_mss(sk, 0); + unsigned int mss = tcp_current_mss(sk); u32 prior_lost = tp->lost_out; tcp_for_write_queue(skb, sk) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 32565803913..c1f259d2d33 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -921,7 +921,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) * factor and mss. */ if (tcp_skb_pcount(skb) > 1) - tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk, 1)); + tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk)); return 0; } @@ -982,15 +982,6 @@ void tcp_mtup_init(struct sock *sk) icsk->icsk_mtup.probe_size = 0; } -/* Bound MSS / TSO packet size with the half of the window */ -static int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) -{ - if (tp->max_window && pktsize > (tp->max_window >> 1)) - return max(tp->max_window >> 1, 68U - tp->tcp_header_len); - else - return pktsize; -} - /* This function synchronize snd mss to current pmtu/exthdr set. tp->rx_opt.user_mss is mss set by user by TCP_MAXSEG. It does NOT counts @@ -1037,22 +1028,17 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu) /* Compute the current effective MSS, taking SACKs and IP options, * and even PMTU discovery events into account. */ -unsigned int tcp_current_mss(struct sock *sk, int large_allowed) +unsigned int tcp_current_mss(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); u32 mss_now; - u16 xmit_size_goal; - int doing_tso = 0; unsigned header_len; struct tcp_out_options opts; struct tcp_md5sig_key *md5; mss_now = tp->mss_cache; - if (large_allowed && sk_can_gso(sk)) - doing_tso = 1; - if (dst) { u32 mtu = dst_mtu(dst); if (mtu != inet_csk(sk)->icsk_pmtu_cookie) @@ -1070,19 +1056,6 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed) mss_now -= delta; } - xmit_size_goal = mss_now; - - if (doing_tso) { - xmit_size_goal = ((sk->sk_gso_max_size - 1) - - inet_csk(sk)->icsk_af_ops->net_header_len - - inet_csk(sk)->icsk_ext_hdr_len - - tp->tcp_header_len); - - xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); - xmit_size_goal -= (xmit_size_goal % mss_now); - } - tp->xmit_size_goal = xmit_size_goal; - return mss_now; } @@ -1264,7 +1237,7 @@ int tcp_may_send_now(struct sock *sk) struct sk_buff *skb = tcp_send_head(sk); return (skb && - tcp_snd_test(sk, skb, tcp_current_mss(sk, 1), + tcp_snd_test(sk, skb, tcp_current_mss(sk), (tcp_skb_is_last(sk, skb) ? tp->nonagle : TCP_NAGLE_PUSH))); } @@ -1421,7 +1394,7 @@ static int tcp_mtu_probe(struct sock *sk) return -1; /* Very simple search strategy: just double the MSS. */ - mss_now = tcp_current_mss(sk, 0); + mss_now = tcp_current_mss(sk); probe_size = 2 * tp->mss_cache; size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high)) { @@ -1903,7 +1876,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) return -EHOSTUNREACH; /* Routing failure or similar. */ - cur_mss = tcp_current_mss(sk, 0); + cur_mss = tcp_current_mss(sk); /* If receiver has shrunk his window, and skb is out of * new window, do not retransmit it. The exception is the @@ -2111,7 +2084,7 @@ void tcp_send_fin(struct sock *sk) * unsent frames. But be careful about outgoing SACKS * and IP options. */ - mss_now = tcp_current_mss(sk, 1); + mss_now = tcp_current_mss(sk); if (tcp_send_head(sk) != NULL) { TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN; @@ -2523,7 +2496,7 @@ int tcp_write_wakeup(struct sock *sk) if ((skb = tcp_send_head(sk)) != NULL && before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) { int err; - unsigned int mss = tcp_current_mss(sk, 0); + unsigned int mss = tcp_current_mss(sk); unsigned int seg_size = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; if (before(tp->pushed_seq, TCP_SKB_CB(skb)->end_seq)) -- cgit v1.2.3-70-g09d2 From ca735b3aaa945626ba65a3e51145bfe4ecd9e222 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Mon, 16 Mar 2009 14:54:21 +0100 Subject: netfilter: use a linked list of loggers This patch modifies nf_log to use a linked list of loggers for each protocol. This list of loggers is read and write protected with a mutex. This patch separates registration and binding. To be used as logging module, a module has to register calling nf_log_register() and to bind to a protocol it has to call nf_log_bind_pf(). This patch also converts the logging modules to the new API. For nfnetlink_log, it simply switchs call to register functions to call to bind function and adds a call to nf_log_register() during init. For other modules, it just remove a const flag from the logger structure and replace it with a __read_mostly. Signed-off-by: Eric Leblond Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_log.h | 11 ++++-- net/ipv4/netfilter/ipt_LOG.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 2 +- net/ipv6/netfilter/ip6t_LOG.c | 2 +- net/netfilter/nf_log.c | 90 ++++++++++++++++++++++++++++-------------- net/netfilter/nfnetlink_log.c | 18 ++++++--- 6 files changed, 84 insertions(+), 41 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index 7182c06974f..920997f1aff 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -1,6 +1,8 @@ #ifndef _NF_LOG_H #define _NF_LOG_H +#include + /* those NF_LOG_* defines and struct nf_loginfo are legacy definitios that will * disappear once iptables is replaced with pkttables. Please DO NOT use them * for any new code! */ @@ -40,12 +42,15 @@ struct nf_logger { struct module *me; nf_logfn *logfn; char *name; + struct list_head list[NFPROTO_NUMPROTO]; }; /* Function to register/unregister log function. */ -int nf_log_register(u_int8_t pf, const struct nf_logger *logger); -void nf_log_unregister(const struct nf_logger *logger); -void nf_log_unregister_pf(u_int8_t pf); +int nf_log_register(u_int8_t pf, struct nf_logger *logger); +void nf_log_unregister(struct nf_logger *logger); + +int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger); +void nf_log_unbind_pf(u_int8_t pf); /* Calls the registered backend logging function */ void nf_log_packet(u_int8_t pf, diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c index 27a78fbbd92..acc44c69eb6 100644 --- a/net/ipv4/netfilter/ipt_LOG.c +++ b/net/ipv4/netfilter/ipt_LOG.c @@ -464,7 +464,7 @@ static struct xt_target log_tg_reg __read_mostly = { .me = THIS_MODULE, }; -static const struct nf_logger ipt_log_logger ={ +static struct nf_logger ipt_log_logger __read_mostly = { .name = "ipt_LOG", .logfn = &ipt_log_packet, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 18a2826b57c..d32cc4bb328 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -379,7 +379,7 @@ static struct xt_target ulog_tg_reg __read_mostly = { .me = THIS_MODULE, }; -static struct nf_logger ipt_ulog_logger = { +static struct nf_logger ipt_ulog_logger __read_mostly = { .name = "ipt_ULOG", .logfn = ipt_logfn, .me = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c index 37adf5abc51..7018cac4fdd 100644 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ b/net/ipv6/netfilter/ip6t_LOG.c @@ -477,7 +477,7 @@ static struct xt_target log_tg6_reg __read_mostly = { .me = THIS_MODULE, }; -static const struct nf_logger ip6t_logger = { +static struct nf_logger ip6t_logger __read_mostly = { .name = "ip6t_LOG", .logfn = &ip6t_log_packet, .me = THIS_MODULE, diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index fa8ae5d2659..a228b5fbcf7 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -16,56 +16,60 @@ #define NF_LOG_PREFIXLEN 128 static const struct nf_logger *nf_loggers[NFPROTO_NUMPROTO] __read_mostly; +static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly; static DEFINE_MUTEX(nf_log_mutex); -/* return EBUSY if somebody else is registered, EEXIST if the same logger - * is registred, 0 on success. */ -int nf_log_register(u_int8_t pf, const struct nf_logger *logger) +static struct nf_logger *__find_logger(int pf, const char *str_logger) { - int ret; + struct nf_logger *t; - if (pf >= ARRAY_SIZE(nf_loggers)) - return -EINVAL; - - /* Any setup of logging members must be done before - * substituting pointer. */ - ret = mutex_lock_interruptible(&nf_log_mutex); - if (ret < 0) - return ret; - - if (!nf_loggers[pf]) - rcu_assign_pointer(nf_loggers[pf], logger); - else if (nf_loggers[pf] == logger) - ret = -EEXIST; - else - ret = -EBUSY; + list_for_each_entry(t, &nf_loggers_l[pf], list[pf]) { + if (!strnicmp(str_logger, t->name, strlen(t->name))) + return t; + } - mutex_unlock(&nf_log_mutex); - return ret; + return NULL; } -EXPORT_SYMBOL(nf_log_register); -void nf_log_unregister_pf(u_int8_t pf) +/* return EEXIST if the same logger is registred, 0 on success. */ +int nf_log_register(u_int8_t pf, struct nf_logger *logger) { + const struct nf_logger *llog; + if (pf >= ARRAY_SIZE(nf_loggers)) - return; + return -EINVAL; + mutex_lock(&nf_log_mutex); - rcu_assign_pointer(nf_loggers[pf], NULL); + + if (pf == NFPROTO_UNSPEC) { + int i; + for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) + list_add_tail(&(logger->list[i]), &(nf_loggers_l[i])); + } else { + /* register at end of list to honor first register win */ + list_add_tail(&logger->list[pf], &nf_loggers_l[pf]); + llog = rcu_dereference(nf_loggers[pf]); + if (llog == NULL) + rcu_assign_pointer(nf_loggers[pf], logger); + } + mutex_unlock(&nf_log_mutex); - /* Give time to concurrent readers. */ - synchronize_rcu(); + return 0; } -EXPORT_SYMBOL(nf_log_unregister_pf); +EXPORT_SYMBOL(nf_log_register); -void nf_log_unregister(const struct nf_logger *logger) +void nf_log_unregister(struct nf_logger *logger) { + const struct nf_logger *c_logger; int i; mutex_lock(&nf_log_mutex); for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) { - if (nf_loggers[i] == logger) + c_logger = rcu_dereference(nf_loggers[i]); + if (c_logger == logger) rcu_assign_pointer(nf_loggers[i], NULL); + list_del(&logger->list[i]); } mutex_unlock(&nf_log_mutex); @@ -73,6 +77,27 @@ void nf_log_unregister(const struct nf_logger *logger) } EXPORT_SYMBOL(nf_log_unregister); +int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger) +{ + mutex_lock(&nf_log_mutex); + if (__find_logger(pf, logger->name) == NULL) { + mutex_unlock(&nf_log_mutex); + return -ENOENT; + } + rcu_assign_pointer(nf_loggers[pf], logger); + mutex_unlock(&nf_log_mutex); + return 0; +} +EXPORT_SYMBOL(nf_log_bind_pf); + +void nf_log_unbind_pf(u_int8_t pf) +{ + mutex_lock(&nf_log_mutex); + rcu_assign_pointer(nf_loggers[pf], NULL); + mutex_unlock(&nf_log_mutex); +} +EXPORT_SYMBOL(nf_log_unbind_pf); + void nf_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, @@ -163,10 +188,15 @@ static const struct file_operations nflog_file_ops = { int __init netfilter_log_init(void) { + int i; #ifdef CONFIG_PROC_FS if (!proc_create("nf_log", S_IRUGO, proc_net_netfilter, &nflog_file_ops)) return -1; #endif + + for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) + INIT_LIST_HEAD(&(nf_loggers_l[i])); + return 0; } diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fa49dc7fe10..3eae3fca29d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -691,7 +691,7 @@ nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, return -ENOTSUPP; } -static const struct nf_logger nfulnl_logger = { +static struct nf_logger nfulnl_logger __read_mostly = { .name = "nfnetlink_log", .logfn = &nfulnl_log_packet, .me = THIS_MODULE, @@ -723,9 +723,9 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, /* Commands without queue context */ switch (cmd->command) { case NFULNL_CFG_CMD_PF_BIND: - return nf_log_register(pf, &nfulnl_logger); + return nf_log_bind_pf(pf, &nfulnl_logger); case NFULNL_CFG_CMD_PF_UNBIND: - nf_log_unregister_pf(pf); + nf_log_unbind_pf(pf); return 0; } } @@ -950,17 +950,25 @@ static int __init nfnetlink_log_init(void) goto cleanup_netlink_notifier; } + status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger); + if (status < 0) { + printk(KERN_ERR "log: failed to register logger\n"); + goto cleanup_subsys; + } + #ifdef CONFIG_PROC_FS if (!proc_create("nfnetlink_log", 0440, proc_net_netfilter, &nful_file_ops)) - goto cleanup_subsys; + goto cleanup_logger; #endif return status; #ifdef CONFIG_PROC_FS +cleanup_logger: + nf_log_unregister(&nfulnl_logger); +#endif cleanup_subsys: nfnetlink_subsys_unregister(&nfulnl_subsys); -#endif cleanup_netlink_notifier: netlink_unregister_notifier(&nfulnl_rtnl_notifier); return status; -- cgit v1.2.3-70-g09d2 From 9d2493f88f846b391a15a736efc7f4b97d6c4046 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Mon, 16 Mar 2009 15:15:35 +0100 Subject: netfilter: remove IPvX specific parts from nf_conntrack_l4proto.h Moving the structure definitions to the corresponding IPvX specific header files. Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_l4proto.h | 5 +---- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 1 + net/netfilter/nf_conntrack_proto_tcp.c | 2 ++ net/netfilter/nf_conntrack_proto_udp.c | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index debdaf75cec..16ab604659e 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -90,10 +90,7 @@ struct nf_conntrack_l4proto struct module *me; }; -/* Existing built-in protocols */ -extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6; -extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4; -extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6; +/* Existing built-in generic protocol */ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; #define MAX_NF_CT_PROTO 256 diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 727b9530448..e6852f61721 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -26,6 +26,7 @@ #include #include #include +#include static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index a1edb9c1ade..7d3944f02ea 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include /* Protects ct->proto.tcp */ static DEFINE_RWLOCK(tcp_lock); diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 2b8b1f579f9..d4021179e24 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ; static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ; -- cgit v1.2.3-70-g09d2 From 0fee54cab7d5ebc58fad8c6a0703c4ea016405e3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 9 Mar 2009 22:07:40 -0400 Subject: cfg80211: remove REGDOM_SET_BY_INIT This is not used as we can always just assume the first regulatory domain set will _always_ be a static regulatory domain. REGDOM_SET_BY_CORE will be the first request from cfg80211 for a regdomain and that then populates the first regulatory request. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/ath9k/regd.c | 1 - include/net/cfg80211.h | 3 --- net/wireless/reg.c | 2 -- 4 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 5268697be79..1d6b05c0d80 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1670,7 +1670,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) } wiphy_apply_custom_regulatory(hw->wiphy, regd); ath9k_reg_apply_radar_flags(hw->wiphy); - ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT); + ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_DRIVER); INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index 639da975bf5..ff0afc02f3c 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -341,7 +341,6 @@ int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) switch (request->initiator) { case REGDOM_SET_BY_DRIVER: - case REGDOM_SET_BY_INIT: case REGDOM_SET_BY_CORE: case REGDOM_SET_BY_USER: break; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 75fa556728c..f195ea46081 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -350,8 +350,6 @@ struct bss_parameters { /** * enum reg_set_by - Indicates who is trying to set the regulatory domain - * @REGDOM_SET_BY_INIT: regulatory domain was set by initialization. We will be - * using a static world regulatory domain by default. * @REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world regulatory domain. * @REGDOM_SET_BY_USER: User asked the wireless core to set the * regulatory domain. @@ -362,7 +360,6 @@ struct bss_parameters { * should consider. */ enum reg_set_by { - REGDOM_SET_BY_INIT, REGDOM_SET_BY_CORE, REGDOM_SET_BY_USER, REGDOM_SET_BY_DRIVER, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index fa738be897a..47ff44751b7 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1255,8 +1255,6 @@ static int ignore_request(struct wiphy *wiphy, return 0; switch (pending_request->initiator) { - case REGDOM_SET_BY_INIT: - return -EINVAL; case REGDOM_SET_BY_CORE: return -EINVAL; case REGDOM_SET_BY_COUNTRY_IE: -- cgit v1.2.3-70-g09d2 From 7db90f4a25bd4184f3d36dfa4f512f53b0448da7 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 9 Mar 2009 22:07:41 -0400 Subject: cfg80211: move enum reg_set_by to nl80211.h We do this so we can later inform userspace who set the regulatory domain and provide details of the request. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/ath9k/regd.c | 31 ++++++++------- drivers/net/wireless/ath9k/regd.h | 3 +- include/linux/nl80211.h | 19 +++++++++ include/net/cfg80211.h | 24 ++---------- net/wireless/core.c | 2 +- net/wireless/core.h | 3 +- net/wireless/reg.c | 82 +++++++++++++++++++++------------------ 8 files changed, 90 insertions(+), 76 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 1d6b05c0d80..e9b3f365f09 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1670,7 +1670,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) } wiphy_apply_custom_regulatory(hw->wiphy, regd); ath9k_reg_apply_radar_flags(hw->wiphy); - ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_DRIVER); + ath9k_reg_apply_world_flags(hw->wiphy, NL80211_REGDOM_SET_BY_DRIVER); INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index ff0afc02f3c..b8f9b6d6bec 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -168,8 +168,9 @@ static bool ath9k_is_radar_freq(u16 center_freq) * received a beacon on a channel we can enable active scan and * adhoc (or beaconing). */ -static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, - enum reg_set_by setby) +static void ath9k_reg_apply_beaconing_flags( + struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { enum ieee80211_band band; struct ieee80211_supported_band *sband; @@ -194,7 +195,7 @@ static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; - if (setby == REGDOM_SET_BY_COUNTRY_IE) { + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule); if (r) @@ -226,8 +227,9 @@ static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, } /* Allows active scan scan on Ch 12 and 13 */ -static void ath9k_reg_apply_active_scan_flags(struct wiphy *wiphy, - enum reg_set_by setby) +static void ath9k_reg_apply_active_scan_flags( + struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; @@ -241,7 +243,7 @@ static void ath9k_reg_apply_active_scan_flags(struct wiphy *wiphy, * If no country IE has been received always enable active scan * on these channels. This is only done for specific regulatory SKUs */ - if (setby != REGDOM_SET_BY_COUNTRY_IE) { + if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { ch = &sband->channels[11]; /* CH 12 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; @@ -308,7 +310,8 @@ void ath9k_reg_apply_radar_flags(struct wiphy *wiphy) } } -void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby) +void ath9k_reg_apply_world_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_wiphy *aphy = hw->priv; @@ -320,11 +323,11 @@ void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby) case 0x63: case 0x66: case 0x67: - ath9k_reg_apply_beaconing_flags(wiphy, setby); + ath9k_reg_apply_beaconing_flags(wiphy, initiator); break; case 0x68: - ath9k_reg_apply_beaconing_flags(wiphy, setby); - ath9k_reg_apply_active_scan_flags(wiphy, setby); + ath9k_reg_apply_beaconing_flags(wiphy, initiator); + ath9k_reg_apply_active_scan_flags(wiphy, initiator); break; } return; @@ -340,11 +343,11 @@ int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath9k_reg_apply_radar_flags(wiphy); switch (request->initiator) { - case REGDOM_SET_BY_DRIVER: - case REGDOM_SET_BY_CORE: - case REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: break; - case REGDOM_SET_BY_COUNTRY_IE: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (ath9k_is_world_regd(sc->sc_ah)) ath9k_reg_apply_world_flags(wiphy, request->initiator); break; diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h index d48160d0c0e..8f885f3bc8d 100644 --- a/drivers/net/wireless/ath9k/regd.h +++ b/drivers/net/wireless/ath9k/regd.h @@ -236,7 +236,8 @@ enum CountryCode { bool ath9k_is_world_regd(struct ath_hw *ah); const struct ieee80211_regdomain *ath9k_world_regdomain(struct ath_hw *ah); const struct ieee80211_regdomain *ath9k_default_world_regdomain(void); -void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby); +void ath9k_reg_apply_world_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator); void ath9k_reg_apply_radar_flags(struct wiphy *wiphy); int ath9k_regd_init(struct ath_hw *ah); bool ath9k_regd_is_eeprom_valid(struct ath_hw *ah); diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f6e56370ea6..c0fd432b57d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -672,6 +672,25 @@ enum nl80211_bitrate_attr { NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + /** * enum nl80211_reg_rule_attr - regulatory rule attributes * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f195ea46081..50f3fd9ff52 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -348,28 +348,10 @@ struct bss_parameters { u8 basic_rates_len; }; -/** - * enum reg_set_by - Indicates who is trying to set the regulatory domain - * @REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world regulatory domain. - * @REGDOM_SET_BY_USER: User asked the wireless core to set the - * regulatory domain. - * @REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the wireless core - * it thinks its knows the regulatory domain we should be in. - * @REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an 802.11 country - * information element with regulatory information it thinks we - * should consider. - */ -enum reg_set_by { - REGDOM_SET_BY_CORE, - REGDOM_SET_BY_USER, - REGDOM_SET_BY_DRIVER, - REGDOM_SET_BY_COUNTRY_IE, -}; - /** * enum environment_cap - Environment parsed from country IE * @ENVIRON_ANY: indicates country IE applies to both indoor and - * outdoor operation. + * outdoor operation. * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation */ @@ -388,7 +370,7 @@ enum environment_cap { * and potentially inform users of which devices specifically * cased the conflicts. * @initiator: indicates who sent this request, could be any of - * of those set in reg_set_by, %REGDOM_SET_BY_* + * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested * regulatory domain. We have a few special codes: * 00 - World regulatory domain @@ -405,7 +387,7 @@ enum environment_cap { */ struct regulatory_request { int wiphy_idx; - enum reg_set_by initiator; + enum nl80211_reg_initiator initiator; char alpha2[2]; bool intersect; u32 country_ie_checksum; diff --git a/net/wireless/core.c b/net/wireless/core.c index dd7f222919f..c939f5ee065 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -350,7 +350,7 @@ int wiphy_register(struct wiphy *wiphy) mutex_lock(&cfg80211_mutex); /* set up regulatory info */ - wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE); + wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); res = device_add(&drv->wiphy.dev); if (res) diff --git a/net/wireless/core.h b/net/wireless/core.h index f6c53f5807f..6acd483a61f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -136,7 +136,8 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); -void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); +void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_reg_initiator setby); void cfg80211_bss_expire(struct cfg80211_registered_device *dev); void cfg80211_bss_age(struct cfg80211_registered_device *dev, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 47ff44751b7..68fde6d33dc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -857,8 +857,8 @@ static int freq_reg_info_regd(struct wiphy *wiphy, * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != REGDOM_SET_BY_USER && + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = wiphy->regd; @@ -943,7 +943,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, * http://tinyurl.com/11d-clarification */ if (r == -ERANGE && - last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { #ifdef CONFIG_CFG80211_REG_DEBUG printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " "intact on %s - no rule found in band on " @@ -956,7 +957,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, * for the band so we respect its band definitions */ #ifdef CONFIG_CFG80211_REG_DEBUG - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) printk(KERN_DEBUG "cfg80211: Disabling " "channel %d MHz on %s due to " "Country IE\n", @@ -970,7 +972,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, power_rule = ®_rule->power_rule; - if (last_request->initiator == REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->strict_regulatory) { /* @@ -1011,11 +1013,12 @@ static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) handle_channel(wiphy, band, i); } -static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) +static bool ignore_reg_update(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { if (!last_request) return true; - if (setby == REGDOM_SET_BY_CORE && + if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->custom_regulatory) return true; /* @@ -1028,12 +1031,12 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) return false; } -static void update_all_wiphy_regulatory(enum reg_set_by setby) +static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { struct cfg80211_registered_device *drv; list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_regulatory(&drv->wiphy, setby); + wiphy_update_regulatory(&drv->wiphy, initiator); } static void handle_reg_beacon(struct wiphy *wiphy, @@ -1124,7 +1127,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->custom_regulatory) return true; return false; @@ -1138,11 +1141,12 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } -void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) +void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { enum ieee80211_band band; - if (ignore_reg_update(wiphy, setby)) + if (ignore_reg_update(wiphy, initiator)) goto out; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) @@ -1255,15 +1259,16 @@ static int ignore_request(struct wiphy *wiphy, return 0; switch (pending_request->initiator) { - case REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_CORE: return -EINVAL; - case REGDOM_SET_BY_COUNTRY_IE: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different @@ -1284,8 +1289,8 @@ static int ignore_request(struct wiphy *wiphy, return -EALREADY; } return REG_INTERSECT; - case REGDOM_SET_BY_DRIVER: - if (last_request->initiator == REGDOM_SET_BY_CORE) { + case NL80211_REGDOM_SET_BY_DRIVER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (is_old_static_regdom(cfg80211_regdomain)) return 0; if (regdom_changes(pending_request->alpha2)) @@ -1298,28 +1303,28 @@ static int ignore_request(struct wiphy *wiphy, * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ - if (last_request->initiator == REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) return -EALREADY; return REG_INTERSECT; - case REGDOM_SET_BY_USER: - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + case NL80211_REGDOM_SET_BY_USER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ - if (last_request->initiator == REGDOM_SET_BY_USER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && last_request->intersect) return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if (last_request->initiator == REGDOM_SET_BY_CORE || - last_request->initiator == REGDOM_SET_BY_DRIVER || - last_request->initiator == REGDOM_SET_BY_USER) { + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || + last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_USER) { if (regdom_changes(last_request->alpha2)) return -EAGAIN; } @@ -1359,7 +1364,8 @@ static int __regulatory_hint(struct wiphy *wiphy, r = ignore_request(wiphy, pending_request); if (r == REG_INTERSECT) { - if (pending_request->initiator == REGDOM_SET_BY_DRIVER) { + if (pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); @@ -1374,7 +1380,8 @@ static int __regulatory_hint(struct wiphy *wiphy, * wiphy */ if (r == -EALREADY && - pending_request->initiator == REGDOM_SET_BY_DRIVER) { + pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); @@ -1425,7 +1432,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - if (reg_request->initiator == REGDOM_SET_BY_DRIVER && + if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); goto out; @@ -1439,7 +1446,7 @@ out: mutex_unlock(&cfg80211_mutex); } -/* Processes regulatory hints, this is all the REGDOM_SET_BY_* */ +/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ static void reg_process_pending_hints(void) { struct regulatory_request *reg_request; @@ -1523,7 +1530,7 @@ static int regulatory_hint_core(const char *alpha2) request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_CORE; + request->initiator = NL80211_REGDOM_SET_BY_CORE; queue_regulatory_request(request); @@ -1544,7 +1551,7 @@ int regulatory_hint_user(const char *alpha2) request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_USER, + request->initiator = NL80211_REGDOM_SET_BY_USER, queue_regulatory_request(request); @@ -1570,7 +1577,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_DRIVER; + request->initiator = NL80211_REGDOM_SET_BY_DRIVER; queue_regulatory_request(request); @@ -1719,7 +1726,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, request->wiphy_idx = get_wiphy_idx(wiphy); request->alpha2[0] = rd->alpha2[0]; request->alpha2[1] = rd->alpha2[1]; - request->initiator = REGDOM_SET_BY_COUNTRY_IE; + request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_checksum = checksum; request->country_ie_env = env; @@ -1827,7 +1834,8 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *drv; drv = cfg80211_drv_by_wiphy_idx( last_request->wiphy_idx); @@ -1919,7 +1927,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { /* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called @@ -1951,7 +1959,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!last_request->intersect) { int r; - if (last_request->initiator != REGDOM_SET_BY_DRIVER) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(); cfg80211_regdomain = rd; return 0; @@ -1975,7 +1983,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) @@ -1986,7 +1994,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ - if (last_request->initiator == REGDOM_SET_BY_DRIVER) + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) request_wiphy->regd = rd; else kfree(rd); -- cgit v1.2.3-70-g09d2 From 711d60a9e7f88e394ccca10f5fc83f95f0cea5b1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Mar 2009 17:30:50 +0100 Subject: netfilter: remove nf_ct_l4proto_find_get/nf_ct_l4proto_put users have been moved to __nf_ct_l4proto_find. Signed-off-by: Florian Westphal Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_l4proto.h | 5 ----- net/netfilter/nf_conntrack_proto.c | 21 --------------------- 2 files changed, 26 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 16ab604659e..b01070bf2f8 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -98,11 +98,6 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; extern struct nf_conntrack_l4proto * __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto); -extern struct nf_conntrack_l4proto * -nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t protocol); - -extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); - /* Protocol registration. */ extern int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *proto); extern void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *proto); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 592d73344d4..9a62b4efa0e 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -74,27 +74,6 @@ EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); /* this is guaranteed to always return a valid protocol helper, since * it falls back to generic_protocol */ -struct nf_conntrack_l4proto * -nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto) -{ - struct nf_conntrack_l4proto *p; - - rcu_read_lock(); - p = __nf_ct_l4proto_find(l3proto, l4proto); - if (!try_module_get(p->me)) - p = &nf_conntrack_l4proto_generic; - rcu_read_unlock(); - - return p; -} -EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); - -void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p) -{ - module_put(p->me); -} -EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); - struct nf_conntrack_l3proto * nf_ct_l3proto_find_get(u_int16_t l3proto) { -- cgit v1.2.3-70-g09d2 From 5e140dfc1fe87eae27846f193086724806b33c7d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 20 Mar 2009 01:33:32 -0700 Subject: net: reorder struct Qdisc for better SMP performance dev_queue_xmit() needs to dirty fields "state", "q", "bstats" and "qstats" On x86_64 arch, they currently span three cache lines, involving more cache line ping pongs than necessary, making longer holding of queue spinlock. We can reduce this to one cache line, by grouping all read-mostly fields at the beginning of structure. (Or should I say, all highly modified fields at the end :) ) Before patch : offsetof(struct Qdisc, state)=0x38 offsetof(struct Qdisc, q)=0x48 offsetof(struct Qdisc, bstats)=0x80 offsetof(struct Qdisc, qstats)=0x90 sizeof(struct Qdisc)=0xc8 After patch : offsetof(struct Qdisc, state)=0x80 offsetof(struct Qdisc, q)=0x88 offsetof(struct Qdisc, bstats)=0xa0 offsetof(struct Qdisc, qstats)=0xac sizeof(struct Qdisc)=0xc0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/gen_stats.h | 2 +- include/net/sch_generic.h | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h index 13f4e74609a..0ffa41df0ee 100644 --- a/include/linux/gen_stats.h +++ b/include/linux/gen_stats.h @@ -22,7 +22,7 @@ struct gnet_stats_basic { __u64 bytes; __u32 packets; -}; +} __attribute__ ((packed)); /** * struct gnet_stats_rate_est - rate estimator diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 3d78a4d2246..964ffa0d881 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -49,18 +49,10 @@ struct Qdisc int padded; struct Qdisc_ops *ops; struct qdisc_size_table *stab; + struct list_head list; u32 handle; u32 parent; atomic_t refcnt; - unsigned long state; - struct sk_buff *gso_skb; - struct sk_buff_head q; - struct netdev_queue *dev_queue; - struct Qdisc *next_sched; - struct list_head list; - - struct gnet_stats_basic bstats; - struct gnet_stats_queue qstats; struct gnet_stats_rate_est rate_est; int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q); @@ -71,6 +63,17 @@ struct Qdisc * and it will live until better solution will be invented. */ struct Qdisc *__parent; + struct netdev_queue *dev_queue; + struct Qdisc *next_sched; + + struct sk_buff *gso_skb; + /* + * For performance sake on SMP, we put highly modified fields at the end + */ + unsigned long state; + struct sk_buff_head q; + struct gnet_stats_basic bstats; + struct gnet_stats_queue qstats; }; struct Qdisc_class_ops -- cgit v1.2.3-70-g09d2 From 04ec5cfcfd9a69c16fc8adb9d7f4836eedb84e53 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Sat, 21 Mar 2009 13:29:05 -0700 Subject: ipv6: reorder struct inet6_ifaddr to remove padding on 64 bit builds reorder struct inet6_ifaddr to remove padding on 64 bit builds remove 8 bytes of padding so inet6_ifaddr becomes 192 bytes & fits into a smaller slab. Signed-off-by: Richard Kennedy Signed-off-by: David S. Miller --- include/net/if_inet6.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index c8effa4b1fe..38b78132019 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -39,8 +39,6 @@ struct inet6_ifaddr __u32 valid_lft; __u32 prefered_lft; - unsigned long cstamp; /* created timestamp */ - unsigned long tstamp; /* updated timestamp */ atomic_t refcnt; spinlock_t lock; @@ -49,6 +47,9 @@ struct inet6_ifaddr __u16 scope; + unsigned long cstamp; /* created timestamp */ + unsigned long tstamp; /* updated timestamp */ + struct timer_list timer; struct inet6_dev *idev; -- cgit v1.2.3-70-g09d2 From 8d2f9e81169b8120cf2b4872930ae491b17c27b8 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Sat, 21 Mar 2009 13:41:09 -0700 Subject: sctp: Clean up TEST_FRAME hacks. Remove 2 TEST_FRAME hacks that are no longer needed. These allowed sctp regression tests to compile before, but are no longer needed. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 7 ------- net/sctp/output.c | 5 +---- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 9e226be3be6..9f80a766828 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -62,13 +62,6 @@ * and will continue to evolve. */ - - -#ifdef TEST_FRAME -#undef CONFIG_SCTP_DBG_OBJCNT -#undef CONFIG_SYSCTL -#endif /* TEST_FRAME */ - #include #include #include diff --git a/net/sctp/output.c b/net/sctp/output.c index 07d58903a74..7d08f522ec8 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -49,13 +49,10 @@ #include #include #include +#include #include #include -#ifndef TEST_FRAME -#include -#endif /* TEST_FRAME (not defined) */ - #include /* for sa_family_t */ #include -- cgit v1.2.3-70-g09d2 From 7ca98fa234afa096ec2a5e7195ad2d32555cca86 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 20 Mar 2009 05:43:14 +0000 Subject: snap: use const for descriptor Protocols should be able to use constant value for the descriptor. Minor whitespace cleanup as well Signed-off-by: Stephen Hemminger Acked-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller --- include/net/psnap.h | 6 +++++- net/802/psnap.c | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/psnap.h b/include/net/psnap.h index b2e01cc3fc8..fe456c295b0 100644 --- a/include/net/psnap.h +++ b/include/net/psnap.h @@ -1,7 +1,11 @@ #ifndef _NET_PSNAP_H #define _NET_PSNAP_H -extern struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *orig_dev)); +extern struct datalink_proto * +register_snap_client(const unsigned char *desc, + int (*rcvfunc)(struct sk_buff *, struct net_device *, + struct packet_type *, + struct net_device *orig_dev)); extern void unregister_snap_client(struct datalink_proto *proto); #endif diff --git a/net/802/psnap.c b/net/802/psnap.c index bdbffa3cb04..6fea0750662 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -29,7 +29,7 @@ static struct llc_sap *snap_sap; /* * Find a snap client by matching the 5 bytes. */ -static struct datalink_proto *find_snap_client(unsigned char *desc) +static struct datalink_proto *find_snap_client(const unsigned char *desc) { struct datalink_proto *proto = NULL, *p; @@ -122,7 +122,7 @@ module_exit(snap_exit); /* * Register SNAP clients. We don't yet use this for IP. */ -struct datalink_proto *register_snap_client(unsigned char *desc, +struct datalink_proto *register_snap_client(const unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *, @@ -137,7 +137,7 @@ struct datalink_proto *register_snap_client(unsigned char *desc, proto = kmalloc(sizeof(*proto), GFP_ATOMIC); if (proto) { - memcpy(proto->type, desc,5); + memcpy(proto->type, desc, 5); proto->rcvfunc = rcvfunc; proto->header_length = 5 + 3; /* snap + 802.2 */ proto->request = snap_request; -- cgit v1.2.3-70-g09d2 From e84665c9cb4db963393fafad6fefe5efdd7e4a09 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 20 Mar 2009 09:52:09 +0000 Subject: dsa: add switch chip cascading support The initial version of the DSA driver only supported a single switch chip per network interface, while DSA-capable switch chips can be interconnected to form a tree of switch chips. This patch adds support for multiple switch chips on a network interface. An example topology for a 16-port device with an embedded CPU is as follows: +-----+ +--------+ +--------+ | |eth0 10| switch |9 10| switch | | CPU +----------+ +-------+ | | | | chip 0 | | chip 1 | +-----+ +---++---+ +---++---+ || || || || ||1000baseT ||1000baseT ||ports 1-8 ||ports 9-16 This requires a couple of interdependent changes in the DSA layer: - The dsa platform driver data needs to be extended: there is still only one netdevice per DSA driver instance (eth0 in the example above), but each of the switch chips in the tree needs its own mii_bus device pointer, MII management bus address, and port name array. (include/net/dsa.h) The existing in-tree dsa users need some small changes to deal with this. (arch/arm) - The DSA and Ethertype DSA tagging modules need to be extended to use the DSA device ID field on receive and demultiplex the packet accordingly, and fill in the DSA device ID field on transmit according to which switch chip the packet is heading to. (net/dsa/tag_{dsa,edsa}.c) - The concept of "CPU port", which is the switch chip port that the CPU is connected to (port 10 on switch chip 0 in the example), needs to be extended with the concept of "upstream port", which is the port on the switch chip that will bring us one hop closer to the CPU (port 10 for both switch chips in the example above). - The dsa platform data needs to specify which ports on which switch chips are links to other switch chips, so that we can enable DSA tagging mode on them. (For inter-switch links, we always use non-EtherType DSA tagging, since it has lower overhead. The CPU link uses dsa or edsa tagging depending on what the 'root' switch chip supports.) This is done by specifying "dsa" for the given port in the port array. - The dsa platform data needs to be extended with information on via which port to reach any given switch chip from any given switch chip. This info is specified via the per-switch chip data struct ->rtable[] array, which gives the nexthop ports for each of the other switches in the tree. For the example topology above, the dsa platform data would look something like this: static struct dsa_chip_data sw[2] = { { .mii_bus = &foo, .sw_addr = 1, .port_names[0] = "p1", .port_names[1] = "p2", .port_names[2] = "p3", .port_names[3] = "p4", .port_names[4] = "p5", .port_names[5] = "p6", .port_names[6] = "p7", .port_names[7] = "p8", .port_names[9] = "dsa", .port_names[10] = "cpu", .rtable = (s8 []){ -1, 9, }, }, { .mii_bus = &foo, .sw_addr = 2, .port_names[0] = "p9", .port_names[1] = "p10", .port_names[2] = "p11", .port_names[3] = "p12", .port_names[4] = "p13", .port_names[5] = "p14", .port_names[6] = "p15", .port_names[7] = "p16", .port_names[10] = "dsa", .rtable = (s8 []){ 10, -1, }, }, }, static struct dsa_platform_data pd = { .netdev = &foo, .nr_switches = 2, .sw = sw, }; Signed-off-by: Lennert Buytenhek Tested-by: Gary Thomas Signed-off-by: David S. Miller --- arch/arm/mach-kirkwood/common.c | 5 +- arch/arm/mach-kirkwood/rd88f6281-setup.c | 13 +- arch/arm/mach-orion5x/common.c | 5 +- arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c | 9 +- arch/arm/mach-orion5x/rd88f5181l-ge-setup.c | 10 +- arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c | 10 +- arch/arm/mach-orion5x/wrt350n-v2-setup.c | 9 +- include/net/dsa.h | 42 +++++-- net/dsa/dsa.c | 177 +++++++++++++++++---------- net/dsa/dsa_priv.h | 97 ++++++++++++--- net/dsa/mv88e6060.c | 12 +- net/dsa/mv88e6123_61_65.c | 92 +++++++++----- net/dsa/mv88e6131.c | 78 +++++++----- net/dsa/slave.c | 25 ++-- net/dsa/tag_dsa.c | 30 +++-- net/dsa/tag_edsa.c | 30 +++-- net/dsa/tag_trailer.c | 10 +- 17 files changed, 443 insertions(+), 211 deletions(-) (limited to 'include/net') diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index b3404b7775b..0d2074f51a5 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -231,14 +231,17 @@ static struct platform_device kirkwood_switch_device = { void __init kirkwood_ge00_switch_init(struct dsa_platform_data *d, int irq) { + int i; + if (irq != NO_IRQ) { kirkwood_switch_resources[0].start = irq; kirkwood_switch_resources[0].end = irq; kirkwood_switch_device.num_resources = 1; } - d->mii_bus = &kirkwood_ge00_shared.dev; d->netdev = &kirkwood_ge00.dev; + for (i = 0; i < d->nr_chips; i++) + d->chip[i].mii_bus = &kirkwood_ge00_shared.dev; kirkwood_switch_device.dev.platform_data = d; platform_device_register(&kirkwood_switch_device); diff --git a/arch/arm/mach-kirkwood/rd88f6281-setup.c b/arch/arm/mach-kirkwood/rd88f6281-setup.c index 9a0e905d10c..e1c0516c4df 100644 --- a/arch/arm/mach-kirkwood/rd88f6281-setup.c +++ b/arch/arm/mach-kirkwood/rd88f6281-setup.c @@ -75,7 +75,7 @@ static struct mv643xx_eth_platform_data rd88f6281_ge00_data = { .duplex = DUPLEX_FULL, }; -static struct dsa_platform_data rd88f6281_switch_data = { +static struct dsa_chip_data rd88f6281_switch_chip_data = { .port_names[0] = "lan1", .port_names[1] = "lan2", .port_names[2] = "lan3", @@ -83,6 +83,11 @@ static struct dsa_platform_data rd88f6281_switch_data = { .port_names[5] = "cpu", }; +static struct dsa_platform_data rd88f6281_switch_plat_data = { + .nr_chips = 1, + .chip = &rd88f6281_switch_chip_data, +}; + static struct mv643xx_eth_platform_data rd88f6281_ge01_data = { .phy_addr = MV643XX_ETH_PHY_ADDR(11), }; @@ -105,12 +110,12 @@ static void __init rd88f6281_init(void) kirkwood_ge00_init(&rd88f6281_ge00_data); kirkwood_pcie_id(&dev, &rev); if (rev == MV88F6281_REV_A0) { - rd88f6281_switch_data.sw_addr = 10; + rd88f6281_switch_chip_data.sw_addr = 10; kirkwood_ge01_init(&rd88f6281_ge01_data); } else { - rd88f6281_switch_data.port_names[4] = "wan"; + rd88f6281_switch_chip_data.port_names[4] = "wan"; } - kirkwood_ge00_switch_init(&rd88f6281_switch_data, NO_IRQ); + kirkwood_ge00_switch_init(&rd88f6281_switch_plat_data, NO_IRQ); kirkwood_rtc_init(); kirkwood_sata_init(&rd88f6281_sata_data); diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 0a623379789..a4ecf625981 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -219,14 +219,17 @@ static struct platform_device orion5x_switch_device = { void __init orion5x_eth_switch_init(struct dsa_platform_data *d, int irq) { + int i; + if (irq != NO_IRQ) { orion5x_switch_resources[0].start = irq; orion5x_switch_resources[0].end = irq; orion5x_switch_device.num_resources = 1; } - d->mii_bus = &orion5x_eth_shared.dev; d->netdev = &orion5x_eth.dev; + for (i = 0; i < d->nr_chips; i++) + d->chip[i].mii_bus = &orion5x_eth_shared.dev; orion5x_switch_device.dev.platform_data = d; platform_device_register(&orion5x_switch_device); diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c index 15f53235ee3..9c1ca41730b 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c @@ -94,7 +94,7 @@ static struct mv643xx_eth_platform_data rd88f5181l_fxo_eth_data = { .duplex = DUPLEX_FULL, }; -static struct dsa_platform_data rd88f5181l_fxo_switch_data = { +static struct dsa_chip_data rd88f5181l_fxo_switch_chip_data = { .port_names[0] = "lan2", .port_names[1] = "lan1", .port_names[2] = "wan", @@ -103,6 +103,11 @@ static struct dsa_platform_data rd88f5181l_fxo_switch_data = { .port_names[7] = "lan3", }; +static struct dsa_platform_data rd88f5181l_fxo_switch_plat_data = { + .nr_chips = 1, + .chip = &rd88f5181l_fxo_switch_chip_data, +}; + static void __init rd88f5181l_fxo_init(void) { /* @@ -117,7 +122,7 @@ static void __init rd88f5181l_fxo_init(void) */ orion5x_ehci0_init(); orion5x_eth_init(&rd88f5181l_fxo_eth_data); - orion5x_eth_switch_init(&rd88f5181l_fxo_switch_data, NO_IRQ); + orion5x_eth_switch_init(&rd88f5181l_fxo_switch_plat_data, NO_IRQ); orion5x_uart0_init(); orion5x_setup_dev_boot_win(RD88F5181L_FXO_NOR_BOOT_BASE, diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c index 8ad3934399d..ee1399ff0ce 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c @@ -95,7 +95,7 @@ static struct mv643xx_eth_platform_data rd88f5181l_ge_eth_data = { .duplex = DUPLEX_FULL, }; -static struct dsa_platform_data rd88f5181l_ge_switch_data = { +static struct dsa_chip_data rd88f5181l_ge_switch_chip_data = { .port_names[0] = "lan2", .port_names[1] = "lan1", .port_names[2] = "wan", @@ -104,6 +104,11 @@ static struct dsa_platform_data rd88f5181l_ge_switch_data = { .port_names[7] = "lan3", }; +static struct dsa_platform_data rd88f5181l_ge_switch_plat_data = { + .nr_chips = 1, + .chip = &rd88f5181l_ge_switch_chip_data, +}; + static struct i2c_board_info __initdata rd88f5181l_ge_i2c_rtc = { I2C_BOARD_INFO("ds1338", 0x68), }; @@ -122,7 +127,8 @@ static void __init rd88f5181l_ge_init(void) */ orion5x_ehci0_init(); orion5x_eth_init(&rd88f5181l_ge_eth_data); - orion5x_eth_switch_init(&rd88f5181l_ge_switch_data, gpio_to_irq(8)); + orion5x_eth_switch_init(&rd88f5181l_ge_switch_plat_data, + gpio_to_irq(8)); orion5x_i2c_init(); orion5x_uart0_init(); diff --git a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c index 262e25e4dac..7737cf9a8f5 100644 --- a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c +++ b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c @@ -35,7 +35,7 @@ static struct mv643xx_eth_platform_data rd88f6183ap_ge_eth_data = { .duplex = DUPLEX_FULL, }; -static struct dsa_platform_data rd88f6183ap_ge_switch_data = { +static struct dsa_chip_data rd88f6183ap_ge_switch_chip_data = { .port_names[0] = "lan1", .port_names[1] = "lan2", .port_names[2] = "lan3", @@ -44,6 +44,11 @@ static struct dsa_platform_data rd88f6183ap_ge_switch_data = { .port_names[5] = "cpu", }; +static struct dsa_platform_data rd88f6183ap_ge_switch_plat_data = { + .nr_chips = 1, + .chip = &rd88f6183ap_ge_switch_chip_data, +}; + static struct mtd_partition rd88f6183ap_ge_partitions[] = { { .name = "kernel", @@ -89,7 +94,8 @@ static void __init rd88f6183ap_ge_init(void) */ orion5x_ehci0_init(); orion5x_eth_init(&rd88f6183ap_ge_eth_data); - orion5x_eth_switch_init(&rd88f6183ap_ge_switch_data, gpio_to_irq(3)); + orion5x_eth_switch_init(&rd88f6183ap_ge_switch_plat_data, + gpio_to_irq(3)); spi_register_board_info(rd88f6183ap_ge_spi_slave_info, ARRAY_SIZE(rd88f6183ap_ge_spi_slave_info)); orion5x_spi_init(); diff --git a/arch/arm/mach-orion5x/wrt350n-v2-setup.c b/arch/arm/mach-orion5x/wrt350n-v2-setup.c index cc8f8920086..1b4ad9d5e2e 100644 --- a/arch/arm/mach-orion5x/wrt350n-v2-setup.c +++ b/arch/arm/mach-orion5x/wrt350n-v2-setup.c @@ -106,7 +106,7 @@ static struct mv643xx_eth_platform_data wrt350n_v2_eth_data = { .duplex = DUPLEX_FULL, }; -static struct dsa_platform_data wrt350n_v2_switch_data = { +static struct dsa_chip_data wrt350n_v2_switch_chip_data = { .port_names[0] = "lan2", .port_names[1] = "lan1", .port_names[2] = "wan", @@ -115,6 +115,11 @@ static struct dsa_platform_data wrt350n_v2_switch_data = { .port_names[7] = "lan4", }; +static struct dsa_platform_data wrt350n_v2_switch_plat_data = { + .nr_chips = 1, + .chip = &wrt350n_v2_switch_chip_data, +}; + static void __init wrt350n_v2_init(void) { /* @@ -129,7 +134,7 @@ static void __init wrt350n_v2_init(void) */ orion5x_ehci0_init(); orion5x_eth_init(&wrt350n_v2_eth_data); - orion5x_eth_switch_init(&wrt350n_v2_switch_data, NO_IRQ); + orion5x_eth_switch_init(&wrt350n_v2_switch_plat_data, NO_IRQ); orion5x_uart0_init(); orion5x_setup_dev_boot_win(WRT350N_V2_NOR_BOOT_BASE, diff --git a/include/net/dsa.h b/include/net/dsa.h index 52e97bfca5a..839f768f9e3 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -1,6 +1,6 @@ /* * include/net/dsa.h - Driver for Distributed Switch Architecture switch chips - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,23 +11,47 @@ #ifndef __LINUX_NET_DSA_H #define __LINUX_NET_DSA_H -#define DSA_MAX_PORTS 12 +#define DSA_MAX_SWITCHES 4 +#define DSA_MAX_PORTS 12 + +struct dsa_chip_data { + /* + * How to access the switch configuration registers. + */ + struct device *mii_bus; + int sw_addr; + + /* + * The names of the switch's ports. Use "cpu" to + * designate the switch port that the cpu is connected to, + * "dsa" to indicate that this port is a DSA link to + * another switch, NULL to indicate the port is unused, + * or any other string to indicate this is a physical port. + */ + char *port_names[DSA_MAX_PORTS]; + + /* + * An array (with nr_chips elements) of which element [a] + * indicates which port on this switch should be used to + * send packets to that are destined for switch a. Can be + * NULL if there is only one switch chip. + */ + s8 *rtable; +}; struct dsa_platform_data { /* * Reference to a Linux network interface that connects - * to the switch chip. + * to the root switch chip of the tree. */ struct device *netdev; /* - * How to access the switch configuration registers, and - * the names of the switch ports (use "cpu" to designate - * the switch port that the cpu is connected to). + * Info structs describing each of the switch chips + * connected via this network interface. */ - struct device *mii_bus; - int sw_addr; - char *port_names[DSA_MAX_PORTS]; + int nr_chips; + struct dsa_chip_data *chip; }; extern bool dsa_uses_dsa_tags(void *dsa_ptr); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 33e99462023..71489f69a42 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1,6 +1,6 @@ /* * net/dsa/dsa.c - Hardware switch handling - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -67,12 +67,13 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name) /* basic switch operations **************************************************/ static struct dsa_switch * -dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, - struct mii_bus *bus, struct net_device *dev) +dsa_switch_setup(struct dsa_switch_tree *dst, int index, + struct device *parent, struct mii_bus *bus) { + struct dsa_chip_data *pd = dst->pd->chip + index; + struct dsa_switch_driver *drv; struct dsa_switch *ds; int ret; - struct dsa_switch_driver *drv; char *name; int i; @@ -81,11 +82,12 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, */ drv = dsa_switch_probe(bus, pd->sw_addr, &name); if (drv == NULL) { - printk(KERN_ERR "%s: could not detect attached switch\n", - dev->name); + printk(KERN_ERR "%s[%d]: could not detect attached switch\n", + dst->master_netdev->name, index); return ERR_PTR(-EINVAL); } - printk(KERN_INFO "%s: detected a %s switch\n", dev->name, name); + printk(KERN_INFO "%s[%d]: detected a %s switch\n", + dst->master_netdev->name, index, name); /* @@ -95,18 +97,16 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, if (ds == NULL) return ERR_PTR(-ENOMEM); - ds->pd = pd; - ds->master_netdev = dev; - ds->master_mii_bus = bus; - + ds->dst = dst; + ds->index = index; + ds->pd = dst->pd->chip + index; ds->drv = drv; - ds->tag_protocol = drv->tag_protocol; + ds->master_mii_bus = bus; /* * Validate supplied switch configuration. */ - ds->cpu_port = -1; for (i = 0; i < DSA_MAX_PORTS; i++) { char *name; @@ -115,32 +115,28 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, continue; if (!strcmp(name, "cpu")) { - if (ds->cpu_port != -1) { + if (dst->cpu_switch != -1) { printk(KERN_ERR "multiple cpu ports?!\n"); ret = -EINVAL; goto out; } - ds->cpu_port = i; + dst->cpu_switch = index; + dst->cpu_port = i; + } else if (!strcmp(name, "dsa")) { + ds->dsa_port_mask |= 1 << i; } else { - ds->valid_port_mask |= 1 << i; + ds->phys_port_mask |= 1 << i; } } - if (ds->cpu_port == -1) { - printk(KERN_ERR "no cpu port?!\n"); - ret = -EINVAL; - goto out; - } - /* - * If we use a tagging format that doesn't have an ethertype - * field, make sure that all packets from this point on get - * sent to the tag format's receive function. (Which will - * discard received packets until we set ds->ports[] below.) + * If the CPU connects to this switch, set the switch tree + * tagging protocol to the preferred tagging format of this + * switch. */ - wmb(); - dev->dsa_ptr = (void *)ds; + if (ds->dst->cpu_switch == index) + ds->dst->tag_protocol = drv->tag_protocol; /* @@ -150,7 +146,7 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, if (ret < 0) goto out; - ret = drv->set_addr(ds, dev->dev_addr); + ret = drv->set_addr(ds, dst->master_netdev->dev_addr); if (ret < 0) goto out; @@ -169,18 +165,18 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, /* * Create network devices for physical switch ports. */ - wmb(); for (i = 0; i < DSA_MAX_PORTS; i++) { struct net_device *slave_dev; - if (!(ds->valid_port_mask & (1 << i))) + if (!(ds->phys_port_mask & (1 << i))) continue; slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]); if (slave_dev == NULL) { - printk(KERN_ERR "%s: can't create dsa slave " - "device for port %d(%s)\n", - dev->name, i, pd->port_names[i]); + printk(KERN_ERR "%s[%d]: can't create dsa " + "slave device for port %d(%s)\n", + dst->master_netdev->name, + index, i, pd->port_names[i]); continue; } @@ -192,7 +188,6 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, out_free: mdiobus_free(ds->slave_mii_bus); out: - dev->dsa_ptr = NULL; kfree(ds); return ERR_PTR(ret); } @@ -212,35 +207,42 @@ static void dsa_switch_destroy(struct dsa_switch *ds) */ bool dsa_uses_dsa_tags(void *dsa_ptr) { - struct dsa_switch *ds = dsa_ptr; + struct dsa_switch_tree *dst = dsa_ptr; - return !!(ds->tag_protocol == htons(ETH_P_DSA)); + return !!(dst->tag_protocol == htons(ETH_P_DSA)); } bool dsa_uses_trailer_tags(void *dsa_ptr) { - struct dsa_switch *ds = dsa_ptr; + struct dsa_switch_tree *dst = dsa_ptr; - return !!(ds->tag_protocol == htons(ETH_P_TRAILER)); + return !!(dst->tag_protocol == htons(ETH_P_TRAILER)); } /* link polling *************************************************************/ static void dsa_link_poll_work(struct work_struct *ugly) { - struct dsa_switch *ds; + struct dsa_switch_tree *dst; + int i; + + dst = container_of(ugly, struct dsa_switch_tree, link_poll_work); - ds = container_of(ugly, struct dsa_switch, link_poll_work); + for (i = 0; i < dst->pd->nr_chips; i++) { + struct dsa_switch *ds = dst->ds[i]; - ds->drv->poll_link(ds); - mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ)); + if (ds != NULL && ds->drv->poll_link != NULL) + ds->drv->poll_link(ds); + } + + mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ)); } -static void dsa_link_poll_timer(unsigned long _ds) +static void dsa_link_poll_timer(unsigned long _dst) { - struct dsa_switch *ds = (void *)_ds; + struct dsa_switch_tree *dst = (void *)_dst; - schedule_work(&ds->link_poll_work); + schedule_work(&dst->link_poll_work); } @@ -303,18 +305,14 @@ static int dsa_probe(struct platform_device *pdev) static int dsa_version_printed; struct dsa_platform_data *pd = pdev->dev.platform_data; struct net_device *dev; - struct mii_bus *bus; - struct dsa_switch *ds; + struct dsa_switch_tree *dst; + int i; if (!dsa_version_printed++) printk(KERN_NOTICE "Distributed Switch Architecture " "driver version %s\n", dsa_driver_version); - if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL) - return -EINVAL; - - bus = dev_to_mii_bus(pd->mii_bus); - if (bus == NULL) + if (pd == NULL || pd->netdev == NULL) return -EINVAL; dev = dev_to_net_device(pd->netdev); @@ -326,36 +324,79 @@ static int dsa_probe(struct platform_device *pdev) return -EEXIST; } - ds = dsa_switch_setup(&pdev->dev, pd, bus, dev); - if (IS_ERR(ds)) { + dst = kzalloc(sizeof(*dst), GFP_KERNEL); + if (dst == NULL) { dev_put(dev); - return PTR_ERR(ds); + return -ENOMEM; } - if (ds->drv->poll_link != NULL) { - INIT_WORK(&ds->link_poll_work, dsa_link_poll_work); - init_timer(&ds->link_poll_timer); - ds->link_poll_timer.data = (unsigned long)ds; - ds->link_poll_timer.function = dsa_link_poll_timer; - ds->link_poll_timer.expires = round_jiffies(jiffies + HZ); - add_timer(&ds->link_poll_timer); + platform_set_drvdata(pdev, dst); + + dst->pd = pd; + dst->master_netdev = dev; + dst->cpu_switch = -1; + dst->cpu_port = -1; + + for (i = 0; i < pd->nr_chips; i++) { + struct mii_bus *bus; + struct dsa_switch *ds; + + bus = dev_to_mii_bus(pd->chip[i].mii_bus); + if (bus == NULL) { + printk(KERN_ERR "%s[%d]: no mii bus found for " + "dsa switch\n", dev->name, i); + continue; + } + + ds = dsa_switch_setup(dst, i, &pdev->dev, bus); + if (IS_ERR(ds)) { + printk(KERN_ERR "%s[%d]: couldn't create dsa switch " + "instance (error %ld)\n", dev->name, i, + PTR_ERR(ds)); + continue; + } + + dst->ds[i] = ds; + if (ds->drv->poll_link != NULL) + dst->link_poll_needed = 1; } - platform_set_drvdata(pdev, ds); + /* + * If we use a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point on get + * sent to the tag format's receive function. + */ + wmb(); + dev->dsa_ptr = (void *)dst; + + if (dst->link_poll_needed) { + INIT_WORK(&dst->link_poll_work, dsa_link_poll_work); + init_timer(&dst->link_poll_timer); + dst->link_poll_timer.data = (unsigned long)dst; + dst->link_poll_timer.function = dsa_link_poll_timer; + dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); + add_timer(&dst->link_poll_timer); + } return 0; } static int dsa_remove(struct platform_device *pdev) { - struct dsa_switch *ds = platform_get_drvdata(pdev); + struct dsa_switch_tree *dst = platform_get_drvdata(pdev); + int i; - if (ds->drv->poll_link != NULL) - del_timer_sync(&ds->link_poll_timer); + if (dst->link_poll_needed) + del_timer_sync(&dst->link_poll_timer); flush_scheduled_work(); - dsa_switch_destroy(ds); + for (i = 0; i < dst->pd->nr_chips; i++) { + struct dsa_switch *ds = dst->ds[i]; + + if (ds != NULL) + dsa_switch_destroy(ds); + } return 0; } diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 7063378a1eb..41055f33d28 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -1,6 +1,6 @@ /* * net/dsa/dsa_priv.h - Hardware switch handling - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,42 +19,107 @@ struct dsa_switch { /* - * Configuration data for the platform device that owns - * this dsa switch instance. + * Parent switch tree, and switch index. */ - struct dsa_platform_data *pd; + struct dsa_switch_tree *dst; + int index; /* - * References to network device and mii bus to use. + * Configuration data for this switch. */ - struct net_device *master_netdev; - struct mii_bus *master_mii_bus; + struct dsa_chip_data *pd; /* - * The used switch driver and frame tagging type. + * The used switch driver. */ struct dsa_switch_driver *drv; - __be16 tag_protocol; + + /* + * Reference to mii bus to use. + */ + struct mii_bus *master_mii_bus; /* * Slave mii_bus and devices for the individual ports. */ - int cpu_port; - u32 valid_port_mask; - struct mii_bus *slave_mii_bus; - struct net_device *ports[DSA_MAX_PORTS]; + u32 dsa_port_mask; + u32 phys_port_mask; + struct mii_bus *slave_mii_bus; + struct net_device *ports[DSA_MAX_PORTS]; +}; + +struct dsa_switch_tree { + /* + * Configuration data for the platform device that owns + * this dsa switch tree instance. + */ + struct dsa_platform_data *pd; + + /* + * Reference to network device to use, and which tagging + * protocol to use. + */ + struct net_device *master_netdev; + __be16 tag_protocol; + + /* + * The switch and port to which the CPU is attached. + */ + s8 cpu_switch; + s8 cpu_port; /* * Link state polling. */ - struct work_struct link_poll_work; - struct timer_list link_poll_timer; + int link_poll_needed; + struct work_struct link_poll_work; + struct timer_list link_poll_timer; + + /* + * Data for the individual switch chips. + */ + struct dsa_switch *ds[DSA_MAX_SWITCHES]; }; +static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) +{ + return !!(ds->index == ds->dst->cpu_switch && p == ds->dst->cpu_port); +} + +static inline u8 dsa_upstream_port(struct dsa_switch *ds) +{ + struct dsa_switch_tree *dst = ds->dst; + + /* + * If this is the root switch (i.e. the switch that connects + * to the CPU), return the cpu port number on this switch. + * Else return the (DSA) port number that connects to the + * switch that is one hop closer to the cpu. + */ + if (dst->cpu_switch == ds->index) + return dst->cpu_port; + else + return ds->pd->rtable[dst->cpu_switch]; +} + struct dsa_slave_priv { + /* + * The linux network interface corresponding to this + * switch port. + */ struct net_device *dev; + + /* + * Which switch this port is a part of, and the port index + * for this port. + */ struct dsa_switch *parent; - int port; + u8 port; + + /* + * The phylib phy_device pointer for the PHY connected + * to this port. + */ struct phy_device *phy; }; diff --git a/net/dsa/mv88e6060.c b/net/dsa/mv88e6060.c index 85081ae9fe8..83277f463af 100644 --- a/net/dsa/mv88e6060.c +++ b/net/dsa/mv88e6060.c @@ -1,6 +1,6 @@ /* * net/dsa/mv88e6060.c - Driver for Marvell 88e6060 switch chips - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,7 +81,7 @@ static int mv88e6060_switch_reset(struct dsa_switch *ds) /* * Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x0A, 0xa130); + REG_WRITE(REG_GLOBAL, 0x0a, 0xa130); /* * Wait up to one second for reset to complete. @@ -128,7 +128,7 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) * state to Forwarding. Additionally, if this is the CPU * port, enable Ingress and Egress Trailer tagging mode. */ - REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x4103 : 0x0003); + REG_WRITE(addr, 0x04, dsa_is_cpu_port(ds, p) ? 0x4103 : 0x0003); /* * Port based VLAN map: give each port its own address @@ -138,9 +138,9 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x06, ((p & 0xf) << 12) | - ((p == ds->cpu_port) ? - ds->valid_port_mask : - (1 << ds->cpu_port))); + (dsa_is_cpu_port(ds, p) ? + ds->phys_port_mask : + (1 << ds->dst->cpu_port))); /* * Port Association Vector: when learning source addresses diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c index 10031872221..52faaa21a4d 100644 --- a/net/dsa/mv88e6123_61_65.c +++ b/net/dsa/mv88e6123_61_65.c @@ -1,6 +1,6 @@ /* * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -98,17 +98,17 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) return ret; /* - * Configure the cpu port, and configure the cpu port as the - * port to which ingress and egress monitor frames are to be - * sent. + * Configure the upstream port, and configure the upstream + * port as the port to which ingress and egress monitor frames + * are to be sent. */ - REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1110)); + REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110)); /* * Disable remote management for now, and set the switch's - * DSA device number to zero. + * DSA device number. */ - REG_WRITE(REG_GLOBAL, 0x1c, 0x0000); + REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f); /* * Send all frames with destination addresses matching @@ -133,10 +133,17 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff); /* - * Map all DSA device IDs to the CPU port. + * Program the DSA routing table. */ - for (i = 0; i < 32; i++) - REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port); + for (i = 0; i < 32; i++) { + int nexthop; + + nexthop = 0x1f; + if (i != ds->index && i < ds->dst->pd->nr_chips) + nexthop = ds->pd->rtable[i] & 0x1f; + + REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop); + } /* * Clear all trunk masks. @@ -176,12 +183,18 @@ static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) { int addr = REG_PORT(p); + u16 val; /* * MAC Forcing register: don't force link, speed, duplex - * or flow control state to any particular values. + * or flow control state to any particular values on physical + * ports, but force the CPU port and all DSA ports to 1000 Mb/s + * full duplex. */ - REG_WRITE(addr, 0x01, 0x0003); + if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) + REG_WRITE(addr, 0x01, 0x003e); + else + REG_WRITE(addr, 0x01, 0x0003); /* * Do not limit the period of time that this port can be @@ -192,37 +205,50 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) /* * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, - * configure the requested (DSA/EDSA) tagging mode if this is - * the CPU port, disable Header mode, enable IGMP/MLD snooping, - * disable VLAN tunneling, determine priority by looking at - * 802.1p and IP priority fields (IP prio has precedence), and - * set STP state to Forwarding. Finally, if this is the CPU - * port, additionally enable forwarding of unknown unicast and - * multicast addresses. - */ - REG_WRITE(addr, 0x04, - (p == ds->cpu_port) ? - (ds->tag_protocol == htons(ETH_P_DSA)) ? - 0x053f : 0x373f : - 0x0433); + * disable Header mode, enable IGMP/MLD snooping, disable VLAN + * tunneling, determine priority by looking at 802.1p and IP + * priority fields (IP prio has precedence), and set STP state + * to Forwarding. + * + * If this is the CPU link, use DSA or EDSA tagging depending + * on which tagging mode was configured. + * + * If this is a link to another switch, use DSA tagging mode. + * + * If this is the upstream port for this switch, enable + * forwarding of unknown unicasts and multicasts. + */ + val = 0x0433; + if (dsa_is_cpu_port(ds, p)) { + if (ds->dst->tag_protocol == htons(ETH_P_EDSA)) + val |= 0x3300; + else + val |= 0x0100; + } + if (ds->dsa_port_mask & (1 << p)) + val |= 0x0100; + if (p == dsa_upstream_port(ds)) + val |= 0x000c; + REG_WRITE(addr, 0x04, val); /* * Port Control 1: disable trunking. Also, if this is the * CPU port, enable learn messages to be sent to this port. */ - REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000); + REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); /* * Port based VLAN map: give each port its own address * database, allow the CPU port to talk to each of the 'real' * ports, and allow each of the 'real' ports to only talk to - * the CPU port. - */ - REG_WRITE(addr, 0x06, - ((p & 0xf) << 12) | - ((p == ds->cpu_port) ? - ds->valid_port_mask : - (1 << ds->cpu_port))); + * the upstream port. + */ + val = (p & 0xf) << 12; + if (dsa_is_cpu_port(ds, p)) + val |= ds->phys_port_mask; + else + val |= 1 << dsa_upstream_port(ds); + REG_WRITE(addr, 0x06, val); /* * Default VLAN ID and priority: don't set a default VLAN diff --git a/net/dsa/mv88e6131.c b/net/dsa/mv88e6131.c index 002995721ec..bb2b41bc854 100644 --- a/net/dsa/mv88e6131.c +++ b/net/dsa/mv88e6131.c @@ -102,17 +102,17 @@ static int mv88e6131_setup_global(struct dsa_switch *ds) REG_WRITE(REG_GLOBAL, 0x19, 0x8100); /* - * Disable ARP mirroring, and configure the cpu port as the - * port to which ingress and egress monitor frames are to be - * sent. + * Disable ARP mirroring, and configure the upstream port as + * the port to which ingress and egress monitor frames are to + * be sent. */ - REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1100) | 0x00f0); + REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1100) | 0x00f0); /* * Disable cascade port functionality, and set the switch's - * DSA device number to zero. + * DSA device number. */ - REG_WRITE(REG_GLOBAL, 0x1c, 0xe000); + REG_WRITE(REG_GLOBAL, 0x1c, 0xe000 | (ds->index & 0x1f)); /* * Send all frames with destination addresses matching @@ -129,10 +129,17 @@ static int mv88e6131_setup_global(struct dsa_switch *ds) REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff); /* - * Map all DSA device IDs to the CPU port. + * Program the DSA routing table. */ - for (i = 0; i < 32; i++) - REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port); + for (i = 0; i < 32; i++) { + int nexthop; + + nexthop = 0x1f; + if (i != ds->index && i < ds->dst->pd->nr_chips) + nexthop = ds->pd->rtable[i] & 0x1f; + + REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop); + } /* * Clear all trunk masks. @@ -158,13 +165,15 @@ static int mv88e6131_setup_global(struct dsa_switch *ds) static int mv88e6131_setup_port(struct dsa_switch *ds, int p) { int addr = REG_PORT(p); + u16 val; /* * MAC Forcing register: don't force link, speed, duplex * or flow control state to any particular values on physical - * ports, but force the CPU port to 1000 Mb/s full duplex. + * ports, but force the CPU port and all DSA ports to 1000 Mb/s + * full duplex. */ - if (p == ds->cpu_port) + if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) REG_WRITE(addr, 0x01, 0x003e); else REG_WRITE(addr, 0x01, 0x0003); @@ -175,29 +184,40 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * enable IGMP/MLD snoop, disable DoubleTag, disable VLAN * tunneling, determine priority by looking at 802.1p and * IP priority fields (IP prio has precedence), and set STP - * state to Forwarding. Finally, if this is the CPU port, - * additionally enable DSA tagging and forwarding of unknown - * unicast addresses. + * state to Forwarding. + * + * If this is the upstream port for this switch, enable + * forwarding of unknown unicasts, and enable DSA tagging + * mode. + * + * If this is the link to another switch, use DSA tagging + * mode, but do not enable forwarding of unknown unicasts. */ - REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x0537 : 0x0433); + val = 0x0433; + if (p == dsa_upstream_port(ds)) + val |= 0x0104; + if (ds->dsa_port_mask & (1 << p)) + val |= 0x0100; + REG_WRITE(addr, 0x04, val); /* * Port Control 1: disable trunking. Also, if this is the * CPU port, enable learn messages to be sent to this port. */ - REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000); + REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); /* * Port based VLAN map: give each port its own address * database, allow the CPU port to talk to each of the 'real' * ports, and allow each of the 'real' ports to only talk to - * the CPU port. + * the upstream port. */ - REG_WRITE(addr, 0x06, - ((p & 0xf) << 12) | - ((p == ds->cpu_port) ? - ds->valid_port_mask : - (1 << ds->cpu_port))); + val = (p & 0xf) << 12; + if (dsa_is_cpu_port(ds, p)) + val |= ds->phys_port_mask; + else + val |= 1 << dsa_upstream_port(ds); + REG_WRITE(addr, 0x06, val); /* * Default VLAN ID and priority: don't set a default VLAN @@ -213,13 +233,15 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * untagged frames on this port, do a destination address * lookup on received packets as usual, don't send a copy * of all transmitted/received frames on this port to the - * CPU, and configure the CPU port number. Also, if this - * is the CPU port, enable forwarding of unknown multicast - * addresses. + * CPU, and configure the upstream port number. + * + * If this is the upstream port for this switch, enable + * forwarding of unknown multicast addresses. */ - REG_WRITE(addr, 0x08, - ((p == ds->cpu_port) ? 0x00c0 : 0x0080) | - ds->cpu_port); + val = 0x0080 | dsa_upstream_port(ds); + if (p == dsa_upstream_port(ds)) + val |= 0x0040; + REG_WRITE(addr, 0x08, val); /* * Rate Control: disable ingress rate limiting. diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 99114e5b32e..ed131181215 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1,6 +1,6 @@ /* * net/dsa/slave.c - Slave device handling - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) { struct dsa_switch *ds = bus->priv; - if (ds->valid_port_mask & (1 << addr)) + if (ds->phys_port_mask & (1 << addr)) return ds->drv->phy_read(ds, addr, reg); return 0xffff; @@ -29,7 +29,7 @@ static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) { struct dsa_switch *ds = bus->priv; - if (ds->valid_port_mask & (1 << addr)) + if (ds->phys_port_mask & (1 << addr)) return ds->drv->phy_write(ds, addr, reg, val); return 0; @@ -43,7 +43,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) ds->slave_mii_bus->write = dsa_slave_phy_write; snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x", ds->master_mii_bus->id, ds->pd->sw_addr); - ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev); + ds->slave_mii_bus->parent = &ds->master_mii_bus->dev; } @@ -51,9 +51,8 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) static int dsa_slave_init(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; - dev->iflink = master->ifindex; + dev->iflink = p->parent->dst->master_netdev->ifindex; return 0; } @@ -61,7 +60,7 @@ static int dsa_slave_init(struct net_device *dev) static int dsa_slave_open(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; + struct net_device *master = p->parent->dst->master_netdev; int err; if (!(master->flags & IFF_UP)) @@ -99,7 +98,7 @@ out: static int dsa_slave_close(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; + struct net_device *master = p->parent->dst->master_netdev; dev_mc_unsync(master, dev); dev_unicast_unsync(master, dev); @@ -117,7 +116,7 @@ static int dsa_slave_close(struct net_device *dev) static void dsa_slave_change_rx_flags(struct net_device *dev, int change) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; + struct net_device *master = p->parent->dst->master_netdev; if (change & IFF_ALLMULTI) dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1); @@ -128,7 +127,7 @@ static void dsa_slave_change_rx_flags(struct net_device *dev, int change) static void dsa_slave_set_rx_mode(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; + struct net_device *master = p->parent->dst->master_netdev; dev_mc_sync(master, dev); dev_unicast_sync(master, dev); @@ -137,7 +136,7 @@ static void dsa_slave_set_rx_mode(struct net_device *dev) static int dsa_slave_set_mac_address(struct net_device *dev, void *a) { struct dsa_slave_priv *p = netdev_priv(dev); - struct net_device *master = p->parent->master_netdev; + struct net_device *master = p->parent->dst->master_netdev; struct sockaddr *addr = a; int err; @@ -341,7 +340,7 @@ struct net_device * dsa_slave_create(struct dsa_switch *ds, struct device *parent, int port, char *name) { - struct net_device *master = ds->master_netdev; + struct net_device *master = ds->dst->master_netdev; struct net_device *slave_dev; struct dsa_slave_priv *p; int ret; @@ -356,7 +355,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN); slave_dev->tx_queue_len = 0; - switch (ds->tag_protocol) { + switch (ds->dst->tag_protocol) { #ifdef CONFIG_NET_DSA_TAG_DSA case htons(ETH_P_DSA): slave_dev->netdev_ops = &dsa_netdev_ops; diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 0b8a91ddff4..8fa25bafe6c 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -1,6 +1,6 @@ /* * net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev) * Construct tagged FROM_CPU DSA tag from 802.1q tag. */ dsa_header = skb->data + 2 * ETH_ALEN; - dsa_header[0] = 0x60; + dsa_header[0] = 0x60 | p->parent->index; dsa_header[1] = p->port << 3; /* @@ -57,7 +57,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev) * Construct untagged FROM_CPU DSA tag. */ dsa_header = skb->data + 2 * ETH_ALEN; - dsa_header[0] = 0x40; + dsa_header[0] = 0x40 | p->parent->index; dsa_header[1] = p->port << 3; dsa_header[2] = 0x00; dsa_header[3] = 0x00; @@ -65,7 +65,7 @@ int dsa_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = htons(ETH_P_DSA); - skb->dev = p->parent->master_netdev; + skb->dev = p->parent->dst->master_netdev; dev_queue_xmit(skb); return NETDEV_TX_OK; @@ -78,11 +78,13 @@ out_free: static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct dsa_switch *ds = dev->dsa_ptr; + struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_switch *ds; u8 *dsa_header; + int source_device; int source_port; - if (unlikely(ds == NULL)) + if (unlikely(dst == NULL)) goto out_drop; skb = skb_unshare(skb, GFP_ATOMIC); @@ -98,16 +100,24 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, dsa_header = skb->data - 2; /* - * Check that frame type is either TO_CPU or FORWARD, and - * that the source device is zero. + * Check that frame type is either TO_CPU or FORWARD. */ - if ((dsa_header[0] & 0xdf) != 0x00 && (dsa_header[0] & 0xdf) != 0xc0) + if ((dsa_header[0] & 0xc0) != 0x00 && (dsa_header[0] & 0xc0) != 0xc0) goto out_drop; /* - * Check that the source port is a registered DSA port. + * Determine source device and port. */ + source_device = dsa_header[0] & 0x1f; source_port = (dsa_header[1] >> 3) & 0x1f; + + /* + * Check that the source device exists and that the source + * port is a registered DSA port. + */ + if (source_device >= dst->pd->nr_chips) + goto out_drop; + ds = dst->ds[source_device]; if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) goto out_drop; diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 16fcb6d196d..815607bd286 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -1,6 +1,6 @@ /* * net/dsa/tag_edsa.c - Ethertype DSA tagging - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev) edsa_header[1] = ETH_P_EDSA & 0xff; edsa_header[2] = 0x00; edsa_header[3] = 0x00; - edsa_header[4] = 0x60; + edsa_header[4] = 0x60 | p->parent->index; edsa_header[5] = p->port << 3; /* @@ -70,7 +70,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev) edsa_header[1] = ETH_P_EDSA & 0xff; edsa_header[2] = 0x00; edsa_header[3] = 0x00; - edsa_header[4] = 0x40; + edsa_header[4] = 0x40 | p->parent->index; edsa_header[5] = p->port << 3; edsa_header[6] = 0x00; edsa_header[7] = 0x00; @@ -78,7 +78,7 @@ int edsa_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = htons(ETH_P_EDSA); - skb->dev = p->parent->master_netdev; + skb->dev = p->parent->dst->master_netdev; dev_queue_xmit(skb); return NETDEV_TX_OK; @@ -91,11 +91,13 @@ out_free: static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct dsa_switch *ds = dev->dsa_ptr; + struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_switch *ds; u8 *edsa_header; + int source_device; int source_port; - if (unlikely(ds == NULL)) + if (unlikely(dst == NULL)) goto out_drop; skb = skb_unshare(skb, GFP_ATOMIC); @@ -111,16 +113,24 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, edsa_header = skb->data + 2; /* - * Check that frame type is either TO_CPU or FORWARD, and - * that the source device is zero. + * Check that frame type is either TO_CPU or FORWARD. */ - if ((edsa_header[0] & 0xdf) != 0x00 && (edsa_header[0] & 0xdf) != 0xc0) + if ((edsa_header[0] & 0xc0) != 0x00 && (edsa_header[0] & 0xc0) != 0xc0) goto out_drop; /* - * Check that the source port is a registered DSA port. + * Determine source device and port. */ + source_device = edsa_header[0] & 0x1f; source_port = (edsa_header[1] >> 3) & 0x1f; + + /* + * Check that the source device exists and that the source + * port is a registered DSA port. + */ + if (source_device >= dst->pd->nr_chips) + goto out_drop; + ds = dst->ds[source_device]; if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) goto out_drop; diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index a6d959da678..1c3e30c38b8 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -1,6 +1,6 @@ /* * net/dsa/tag_trailer.c - Trailer tag format handling - * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2008-2009 Marvell Semiconductor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,7 +59,7 @@ int trailer_xmit(struct sk_buff *skb, struct net_device *dev) nskb->protocol = htons(ETH_P_TRAILER); - nskb->dev = p->parent->master_netdev; + nskb->dev = p->parent->dst->master_netdev; dev_queue_xmit(nskb); return NETDEV_TX_OK; @@ -68,12 +68,14 @@ int trailer_xmit(struct sk_buff *skb, struct net_device *dev) static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct dsa_switch *ds = dev->dsa_ptr; + struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_switch *ds; u8 *trailer; int source_port; - if (unlikely(ds == NULL)) + if (unlikely(dst == NULL)) goto out_drop; + ds = dst->ds[0]; skb = skb_unshare(skb, GFP_ATOMIC); if (skb == NULL) -- cgit v1.2.3-70-g09d2 From b2f5e7cd3dee2ed721bf0675e1a1ddebb849aee6 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 24 Mar 2009 16:24:51 +0000 Subject: ipv6: Fix conflict resolutions during ipv6 binding The ipv6 version of bind_conflict code calls ipv6_rcv_saddr_equal() which at times wrongly identified intersections between addresses. It particularly broke down under a few instances and caused erroneous bind conflicts. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/addrconf.h | 4 ++-- include/net/udp.h | 2 ++ net/ipv4/udp.c | 3 ++- net/ipv6/addrconf.c | 34 ---------------------------------- net/ipv6/udp.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 34 insertions(+), 37 deletions(-) (limited to 'include/net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index c216de528b0..7b55ab215a6 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -88,8 +88,8 @@ extern int ipv6_dev_get_saddr(struct net *net, extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, unsigned char banned_flags); -extern int ipv6_rcv_saddr_equal(const struct sock *sk, - const struct sock *sk2); +extern int ipv6_rcv_saddr_equal(const struct sock *sk, + const struct sock *sk2); extern void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr); extern void addrconf_leave_solict(struct inet6_dev *idev, diff --git a/include/net/udp.h b/include/net/udp.h index 90e6ce56be6..93dbe294d45 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -124,6 +124,8 @@ static inline void udp_lib_close(struct sock *sk, long timeout) sk_common_release(sk); } +extern int ipv4_rcv_saddr_equal(const struct sock *sk1, + const struct sock *sk2); extern int udp_lib_get_port(struct sock *sk, unsigned short snum, int (*)(const struct sock*,const struct sock*)); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 05b7abb99f6..ace2ac8a42f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -222,7 +222,7 @@ fail: return error; } -static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) +int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) { struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); @@ -1819,6 +1819,7 @@ EXPORT_SYMBOL(udp_lib_getsockopt); EXPORT_SYMBOL(udp_lib_setsockopt); EXPORT_SYMBOL(udp_poll); EXPORT_SYMBOL(udp_lib_get_port); +EXPORT_SYMBOL(ipv4_rcv_saddr_equal); #ifdef CONFIG_PROC_FS EXPORT_SYMBOL(udp_proc_register); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8499da9e76a..a8218bc1806 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1370,40 +1370,6 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add return ifp; } -int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) -{ - const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr; - const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); - __be32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr; - __be32 sk2_rcv_saddr = inet_rcv_saddr(sk2); - int sk_ipv6only = ipv6_only_sock(sk); - int sk2_ipv6only = inet_v6_ipv6only(sk2); - int addr_type = ipv6_addr_type(sk_rcv_saddr6); - int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; - - if (!sk2_rcv_saddr && !sk_ipv6only) - return 1; - - if (addr_type2 == IPV6_ADDR_ANY && - !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED)) - return 1; - - if (addr_type == IPV6_ADDR_ANY && - !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED)) - return 1; - - if (sk2_rcv_saddr6 && - ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6)) - return 1; - - if (addr_type == IPV6_ADDR_MAPPED && - !sk2_ipv6only && - (!sk2_rcv_saddr || !sk_rcv_saddr || sk_rcv_saddr == sk2_rcv_saddr)) - return 1; - - return 0; -} - /* Gets referenced address, destroys ifaddr */ static void addrconf_dad_stop(struct inet6_ifaddr *ifp) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 84b1a296eec..6842dd2edd5 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -49,6 +49,34 @@ #include #include "udp_impl.h" +int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) +{ + const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr; + const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); + int sk_ipv6only = ipv6_only_sock(sk); + int sk2_ipv6only = inet_v6_ipv6only(sk2); + int addr_type = ipv6_addr_type(sk_rcv_saddr6); + int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; + + /* if both are mapped, treat as IPv4 */ + if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED) + return ipv4_rcv_saddr_equal(sk, sk2); + + if (addr_type2 == IPV6_ADDR_ANY && + !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED)) + return 1; + + if (addr_type == IPV6_ADDR_ANY && + !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED)) + return 1; + + if (sk2_rcv_saddr6 && + ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6)) + return 1; + + return 0; +} + int udp_v6_get_port(struct sock *sk, unsigned short snum) { return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal); -- cgit v1.2.3-70-g09d2 From 67fca028f1535e510689d2e444b0289e264e05c1 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 24 Mar 2009 23:32:03 -0700 Subject: ax88796: Add method to take MAC from platform data Implement a way to provide the MAC address for ax88796 devices from their platform data. Boards might decide to set the address programmatically, taken from boot tags or other sources. Signed-off-by: Daniel Mack Signed-off-by: David S. Miller --- drivers/net/ax88796.c | 17 ++++++++++++----- include/net/ax88796.h | 13 ++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ax88796.c b/drivers/net/ax88796.c index e7c9748437d..62d9c9cc567 100644 --- a/drivers/net/ax88796.c +++ b/drivers/net/ax88796.c @@ -733,12 +733,19 @@ static int ax_init_dev(struct net_device *dev, int first_init) /* load the mac-address from the device if this is the * first time we've initialised */ - if (first_init && ax->plat->flags & AXFLG_MAC_FROMDEV) { - ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, - ei_local->mem + E8390_CMD); /* 0x61 */ + if (first_init) { + if (ax->plat->flags & AXFLG_MAC_FROMDEV) { + ei_outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, + ei_local->mem + E8390_CMD); /* 0x61 */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + dev->dev_addr[i] = + ei_inb(ioaddr + EN1_PHYS_SHIFT(i)); + } - for (i = 0 ; i < ETHER_ADDR_LEN ; i++) - dev->dev_addr[i] = ei_inb(ioaddr + EN1_PHYS_SHIFT(i)); + if ((ax->plat->flags & AXFLG_MAC_FROMPLATFORM) && + ax->plat->mac_addr) + memcpy(dev->dev_addr, ax->plat->mac_addr, + ETHER_ADDR_LEN); } ax_reset_8390(dev); diff --git a/include/net/ax88796.h b/include/net/ax88796.h index 51329dae44e..b9a3beca0ce 100644 --- a/include/net/ax88796.h +++ b/include/net/ax88796.h @@ -15,14 +15,17 @@ #define AXFLG_HAS_EEPROM (1<<0) #define AXFLG_MAC_FROMDEV (1<<1) /* device already has MAC */ #define AXFLG_HAS_93CX6 (1<<2) /* use eeprom_93cx6 driver */ +#define AXFLG_MAC_FROMPLATFORM (1<<3) /* MAC given by platform data */ struct ax_plat_data { unsigned int flags; - unsigned char wordlength; /* 1 or 2 */ - unsigned char dcr_val; /* default value for DCR */ - unsigned char rcr_val; /* default value for RCR */ - unsigned char gpoc_val; /* default value for GPOC */ - u32 *reg_offsets; /* register offsets */ + unsigned char wordlength; /* 1 or 2 */ + unsigned char dcr_val; /* default value for DCR */ + unsigned char rcr_val; /* default value for RCR */ + unsigned char gpoc_val; /* default value for GPOC */ + u32 *reg_offsets; /* register offsets */ + u8 *mac_addr; /* MAC addr (only used when + AXFLG_MAC_FROMPLATFORM is used */ }; #endif /* __NET_AX88796_PLAT_H */ -- cgit v1.2.3-70-g09d2 From d0dba7255b541f1651a88e75ebdb20dd45509c2f Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 25 Mar 2009 18:24:48 +0100 Subject: netfilter: ctnetlink: add callbacks to the per-proto nlattrs There is added a single callback for the l3 proto helper. The two callbacks for the l4 protos are necessary because of the general structure of a ctnetlink event, which is in short: CTA_TUPLE_ORIG CTA_TUPLE_REPLY CTA_ID ... CTA_PROTOINFO CTA_TUPLE_MASTER Therefore the formular is size := sizeof(generic-nlas) + 3 * sizeof(tuple_nlas) + sizeof(protoinfo_nlas) Some of the NLAs are optional, e. g. CTA_TUPLE_MASTER, which is only set if it's an expected connection. But the number of optional NLAs is small enough to prevent netlink_trim() from reallocating if calculated properly. Signed-off-by: Holger Eitzenberger Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_l3proto.h | 7 +++++++ include/net/netfilter/nf_conntrack_l4proto.h | 6 ++++++ net/netfilter/nf_conntrack_proto.c | 16 ++++++++++++++++ 3 files changed, 29 insertions(+) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 0378676c3dd..9f99d36d5de 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -53,10 +53,17 @@ struct nf_conntrack_l3proto int (*tuple_to_nlattr)(struct sk_buff *skb, const struct nf_conntrack_tuple *t); + /* + * Calculate size of tuple nlattr + */ + int (*nlattr_tuple_size)(void); + int (*nlattr_to_tuple)(struct nlattr *tb[], struct nf_conntrack_tuple *t); const struct nla_policy *nla_policy; + size_t nla_size; + #ifdef CONFIG_SYSCTL struct ctl_table_header *ctl_table_header; struct ctl_path *ctl_table_path; diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index b01070bf2f8..a120990b3b2 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -64,16 +64,22 @@ struct nf_conntrack_l4proto /* convert protoinfo to nfnetink attributes */ int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla, const struct nf_conn *ct); + /* Calculate protoinfo nlattr size */ + int (*nlattr_size)(void); /* convert nfnetlink attributes to protoinfo */ int (*from_nlattr)(struct nlattr *tb[], struct nf_conn *ct); int (*tuple_to_nlattr)(struct sk_buff *skb, const struct nf_conntrack_tuple *t); + /* Calculate tuple nlattr size */ + int (*nlattr_tuple_size)(void); int (*nlattr_to_tuple)(struct nlattr *tb[], struct nf_conntrack_tuple *t); const struct nla_policy *nla_policy; + size_t nla_size; + #ifdef CONFIG_SYSCTL struct ctl_table_header **ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 9a62b4efa0e..1a4568bf7ea 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -167,6 +167,9 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) if (proto->l3proto >= AF_MAX) return -EBUSY; + if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) + return -EINVAL; + mutex_lock(&nf_ct_proto_mutex); if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) { ret = -EBUSY; @@ -177,6 +180,9 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) if (ret < 0) goto out_unlock; + if (proto->nlattr_tuple_size) + proto->nla_size = 3 * proto->nlattr_tuple_size(); + rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); out_unlock: @@ -263,6 +269,10 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) if (l4proto->l3proto >= PF_MAX) return -EBUSY; + if ((l4proto->to_nlattr && !l4proto->nlattr_size) + || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) + return -EINVAL; + mutex_lock(&nf_ct_proto_mutex); if (!nf_ct_protos[l4proto->l3proto]) { /* l3proto may be loaded latter. */ @@ -290,6 +300,12 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) if (ret < 0) goto out_unlock; + l4proto->nla_size = 0; + if (l4proto->nlattr_size) + l4proto->nla_size += l4proto->nlattr_size(); + if (l4proto->nlattr_tuple_size) + l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); + rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); -- cgit v1.2.3-70-g09d2 From e487eb99cf9381a4f8254fa01747a85818da612b Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 25 Mar 2009 18:26:30 +0100 Subject: netlink: add nla_policy_len() It calculates the max. length of a Netlink policy, which is usefull for allocating Netlink buffers roughly the size of the actual message. Signed-off-by: Holger Eitzenberger Signed-off-by: Patrick McHardy --- include/net/netlink.h | 1 + net/netlink/attr.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include/net') diff --git a/include/net/netlink.h b/include/net/netlink.h index 8a6150a3f4c..eddb50289d6 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -230,6 +230,7 @@ extern int nla_validate(struct nlattr *head, int len, int maxtype, extern int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy); +extern int nla_policy_len(const struct nla_policy *, int); extern struct nlattr * nla_find(struct nlattr *head, int len, int attrtype); extern size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); diff --git a/net/netlink/attr.c b/net/netlink/attr.c index 56c3ce7fe29..ae32c573df0 100644 --- a/net/netlink/attr.c +++ b/net/netlink/attr.c @@ -132,6 +132,32 @@ errout: return err; } +/** + * nla_policy_len - Determin the max. length of a policy + * @policy: policy to use + * @n: number of policies + * + * Determines the max. length of the policy. It is currently used + * to allocated Netlink buffers roughly the size of the actual + * message. + * + * Returns 0 on success or a negative error code. + */ +int +nla_policy_len(const struct nla_policy *p, int n) +{ + int i, len = 0; + + for (i = 0; i < n; i++) { + if (p->len) + len += nla_total_size(p->len); + else if (nla_attr_minlen[p->type]) + len += nla_total_size(nla_attr_minlen[p->type]); + } + + return len; +} + /** * nla_parse - Parse a stream of attributes into a tb buffer * @tb: destination array with maxtype+1 elements @@ -456,6 +482,7 @@ int nla_append(struct sk_buff *skb, int attrlen, const void *data) } EXPORT_SYMBOL(nla_validate); +EXPORT_SYMBOL(nla_policy_len); EXPORT_SYMBOL(nla_parse); EXPORT_SYMBOL(nla_find); EXPORT_SYMBOL(nla_strlcpy); -- cgit v1.2.3-70-g09d2 From af9d32ad6718b9a80fa89f557cc1fbb63a93ec15 Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 25 Mar 2009 18:44:01 +0100 Subject: netfilter: limit the length of the helper name This is necessary in order to have an upper bound for Netlink message calculation, which is not a problem at all, as there are no helpers with a longer name. Signed-off-by: Holger Eitzenberger Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_helper.h | 2 ++ net/netfilter/nf_conntrack_helper.c | 1 + 2 files changed, 3 insertions(+) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 66d65a7caa3..ee2a4b369a0 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -14,6 +14,8 @@ struct module; +#define NF_CT_HELPER_NAME_LEN 16 + struct nf_conntrack_helper { struct hlist_node hnode; /* Internal use. */ diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index a51bdac9f3a..805cfdd4230 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -142,6 +142,7 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) BUG_ON(me->expect_policy == NULL); BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); + BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); mutex_lock(&nf_ct_helper_mutex); hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); -- cgit v1.2.3-70-g09d2 From ea781f197d6a835cbb93a0bf88ee1696296ed8aa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 25 Mar 2009 21:05:46 +0100 Subject: netfilter: nf_conntrack: use SLAB_DESTROY_BY_RCU and get rid of call_rcu() Use "hlist_nulls" infrastructure we added in 2.6.29 for RCUification of UDP & TCP. This permits an easy conversion from call_rcu() based hash lists to a SLAB_DESTROY_BY_RCU one. Avoiding call_rcu() delay at nf_conn freeing time has numerous gains. First, it doesnt fill RCU queues (up to 10000 elements per cpu). This reduces OOM possibility, if queued elements are not taken into account This reduces latency problems when RCU queue size hits hilimit and triggers emergency mode. - It allows fast reuse of just freed elements, permitting better use of CPU cache. - We delete rcu_head from "struct nf_conn", shrinking size of this structure by 8 or 16 bytes. This patch only takes care of "struct nf_conn". call_rcu() is still used for less critical conntrack parts, that may be converted later if necessary. Signed-off-by: Eric Dumazet Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 14 ++- include/net/netfilter/nf_conntrack_tuple.h | 6 +- include/net/netns/conntrack.h | 5 +- .../netfilter/nf_conntrack_l3proto_ipv4_compat.c | 63 ++++++----- net/ipv4/netfilter/nf_nat_core.c | 2 +- net/netfilter/nf_conntrack_core.c | 123 ++++++++++++--------- net/netfilter/nf_conntrack_expect.c | 2 +- net/netfilter/nf_conntrack_helper.c | 7 +- net/netfilter/nf_conntrack_netlink.c | 20 ++-- net/netfilter/nf_conntrack_standalone.c | 57 ++++++---- net/netfilter/xt_connlimit.c | 6 +- 11 files changed, 174 insertions(+), 131 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 4dfb793c3f1..6c3f964de9e 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -91,8 +91,7 @@ struct nf_conn_help { #include #include -struct nf_conn -{ +struct nf_conn { /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, plus 1 for any connection(s) we are `master' for */ struct nf_conntrack ct_general; @@ -126,7 +125,6 @@ struct nf_conn #ifdef CONFIG_NET_NS struct net *ct_net; #endif - struct rcu_head rcu; }; static inline struct nf_conn * @@ -190,9 +188,13 @@ static inline void nf_ct_put(struct nf_conn *ct) extern int nf_ct_l3proto_try_module_get(unsigned short l3proto); extern void nf_ct_l3proto_module_put(unsigned short l3proto); -extern struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced); -extern void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced, - unsigned int size); +/* + * Allocate a hashtable of hlist_head (if nulls == 0), + * or hlist_nulls_head (if nulls == 1) + */ +extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls); + +extern void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size); extern struct nf_conntrack_tuple_hash * __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple); diff --git a/include/net/netfilter/nf_conntrack_tuple.h b/include/net/netfilter/nf_conntrack_tuple.h index f2f6aa73dc1..2628c154d40 100644 --- a/include/net/netfilter/nf_conntrack_tuple.h +++ b/include/net/netfilter/nf_conntrack_tuple.h @@ -12,6 +12,7 @@ #include #include +#include /* A `tuple' is a structure containing the information to uniquely identify a connection. ie. if two packets have the same tuple, they @@ -146,9 +147,8 @@ static inline void nf_ct_dump_tuple(const struct nf_conntrack_tuple *t) ((enum ip_conntrack_dir)(h)->tuple.dst.dir) /* Connections have two entries in the hash table: one for each way */ -struct nf_conntrack_tuple_hash -{ - struct hlist_node hnode; +struct nf_conntrack_tuple_hash { + struct hlist_nulls_node hnnode; struct nf_conntrack_tuple tuple; }; diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index f4498a62881..9dc58402bc0 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -2,6 +2,7 @@ #define __NETNS_CONNTRACK_H #include +#include #include struct ctl_table_header; @@ -10,9 +11,9 @@ struct nf_conntrack_ecache; struct netns_ct { atomic_t count; unsigned int expect_count; - struct hlist_head *hash; + struct hlist_nulls_head *hash; struct hlist_head *expect_hash; - struct hlist_head unconfirmed; + struct hlist_nulls_head unconfirmed; struct ip_conntrack_stat *stat; #ifdef CONFIG_NF_CONNTRACK_EVENTS struct nf_conntrack_ecache *ecache; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 6ba5c557690..8668a3defda 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -25,40 +25,42 @@ struct ct_iter_state { unsigned int bucket; }; -static struct hlist_node *ct_get_first(struct seq_file *seq) +static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) { struct net *net = seq_file_net(seq); struct ct_iter_state *st = seq->private; - struct hlist_node *n; + struct hlist_nulls_node *n; for (st->bucket = 0; st->bucket < nf_conntrack_htable_size; st->bucket++) { n = rcu_dereference(net->ct.hash[st->bucket].first); - if (n) + if (!is_a_nulls(n)) return n; } return NULL; } -static struct hlist_node *ct_get_next(struct seq_file *seq, - struct hlist_node *head) +static struct hlist_nulls_node *ct_get_next(struct seq_file *seq, + struct hlist_nulls_node *head) { struct net *net = seq_file_net(seq); struct ct_iter_state *st = seq->private; head = rcu_dereference(head->next); - while (head == NULL) { - if (++st->bucket >= nf_conntrack_htable_size) - return NULL; + while (is_a_nulls(head)) { + if (likely(get_nulls_value(head) == st->bucket)) { + if (++st->bucket >= nf_conntrack_htable_size) + return NULL; + } head = rcu_dereference(net->ct.hash[st->bucket].first); } return head; } -static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos) +static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos) { - struct hlist_node *head = ct_get_first(seq); + struct hlist_nulls_node *head = ct_get_first(seq); if (head) while (pos && (head = ct_get_next(seq, head))) @@ -87,69 +89,76 @@ static void ct_seq_stop(struct seq_file *s, void *v) static int ct_seq_show(struct seq_file *s, void *v) { - const struct nf_conntrack_tuple_hash *hash = v; - const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); + struct nf_conntrack_tuple_hash *hash = v; + struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); const struct nf_conntrack_l3proto *l3proto; const struct nf_conntrack_l4proto *l4proto; + int ret = 0; NF_CT_ASSERT(ct); + if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) + return 0; + /* we only want to print DIR_ORIGINAL */ if (NF_CT_DIRECTION(hash)) - return 0; + goto release; if (nf_ct_l3num(ct) != AF_INET) - return 0; + goto release; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); NF_CT_ASSERT(l3proto); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); NF_CT_ASSERT(l4proto); + ret = -ENOSPC; if (seq_printf(s, "%-8s %u %ld ", l4proto->name, nf_ct_protonum(ct), timer_pending(&ct->timeout) ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0) - return -ENOSPC; + goto release; if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct)) - return -ENOSPC; + goto release; if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, l3proto, l4proto)) - return -ENOSPC; + goto release; if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) - return -ENOSPC; + goto release; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) if (seq_printf(s, "[UNREPLIED] ")) - return -ENOSPC; + goto release; if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, l3proto, l4proto)) - return -ENOSPC; + goto release; if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) - return -ENOSPC; + goto release; if (test_bit(IPS_ASSURED_BIT, &ct->status)) if (seq_printf(s, "[ASSURED] ")) - return -ENOSPC; + goto release; #ifdef CONFIG_NF_CONNTRACK_MARK if (seq_printf(s, "mark=%u ", ct->mark)) - return -ENOSPC; + goto release; #endif #ifdef CONFIG_NF_CONNTRACK_SECMARK if (seq_printf(s, "secmark=%u ", ct->secmark)) - return -ENOSPC; + goto release; #endif if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) - return -ENOSPC; - - return 0; + goto release; + ret = 0; +release: + nf_ct_put(ct); + return ret; } static const struct seq_operations ct_seq_ops = { diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index a65cf692359..fe65187810f 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -679,7 +679,7 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct, static int __net_init nf_nat_net_init(struct net *net) { net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size, - &net->ipv4.nat_vmalloced); + &net->ipv4.nat_vmalloced, 0); if (!net->ipv4.nat_bysource) return -ENOMEM; return 0; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 54e983f1389..c55bbdc7d42 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -163,8 +164,8 @@ static void clean_from_lists(struct nf_conn *ct) { pr_debug("clean_from_lists(%p)\n", ct); - hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode); - hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode); + hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); + hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode); /* Destroy all pending expectations */ nf_ct_remove_expectations(ct); @@ -204,8 +205,8 @@ destroy_conntrack(struct nf_conntrack *nfct) /* We overload first tuple to link into unconfirmed list. */ if (!nf_ct_is_confirmed(ct)) { - BUG_ON(hlist_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode)); - hlist_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode); + BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); + hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); } NF_CT_STAT_INC(net, delete); @@ -242,18 +243,26 @@ static void death_by_timeout(unsigned long ul_conntrack) nf_ct_put(ct); } +/* + * Warning : + * - Caller must take a reference on returned object + * and recheck nf_ct_tuple_equal(tuple, &h->tuple) + * OR + * - Caller must lock nf_conntrack_lock before calling this function + */ struct nf_conntrack_tuple_hash * __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple) { struct nf_conntrack_tuple_hash *h; - struct hlist_node *n; + struct hlist_nulls_node *n; unsigned int hash = hash_conntrack(tuple); /* Disable BHs the entire time since we normally need to disable them * at least once for the stats anyway. */ local_bh_disable(); - hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnode) { +begin: + hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) { if (nf_ct_tuple_equal(tuple, &h->tuple)) { NF_CT_STAT_INC(net, found); local_bh_enable(); @@ -261,6 +270,13 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple) } NF_CT_STAT_INC(net, searched); } + /* + * if the nulls value we got at the end of this lookup is + * not the expected one, we must restart lookup. + * We probably met an item that was moved to another chain. + */ + if (get_nulls_value(n) != hash) + goto begin; local_bh_enable(); return NULL; @@ -275,11 +291,18 @@ nf_conntrack_find_get(struct net *net, const struct nf_conntrack_tuple *tuple) struct nf_conn *ct; rcu_read_lock(); +begin: h = __nf_conntrack_find(net, tuple); if (h) { ct = nf_ct_tuplehash_to_ctrack(h); if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) h = NULL; + else { + if (unlikely(!nf_ct_tuple_equal(tuple, &h->tuple))) { + nf_ct_put(ct); + goto begin; + } + } } rcu_read_unlock(); @@ -293,9 +316,9 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct, { struct net *net = nf_ct_net(ct); - hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode, + hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, &net->ct.hash[hash]); - hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode, + hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &net->ct.hash[repl_hash]); } @@ -318,7 +341,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct nf_conn_help *help; - struct hlist_node *n; + struct hlist_nulls_node *n; enum ip_conntrack_info ctinfo; struct net *net; @@ -350,17 +373,17 @@ __nf_conntrack_confirm(struct sk_buff *skb) /* See if there's one in the list already, including reverse: NAT could have grabbed it without realizing, since we're not in the hash. If there is, we lost race. */ - hlist_for_each_entry(h, n, &net->ct.hash[hash], hnode) + hlist_nulls_for_each_entry(h, n, &net->ct.hash[hash], hnnode) if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, &h->tuple)) goto out; - hlist_for_each_entry(h, n, &net->ct.hash[repl_hash], hnode) + hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode) if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, &h->tuple)) goto out; /* Remove from unconfirmed list */ - hlist_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode); + hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); __nf_conntrack_hash_insert(ct, hash, repl_hash); /* Timer relative to confirmation time, not original @@ -399,14 +422,14 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, { struct net *net = nf_ct_net(ignored_conntrack); struct nf_conntrack_tuple_hash *h; - struct hlist_node *n; + struct hlist_nulls_node *n; unsigned int hash = hash_conntrack(tuple); /* Disable BHs the entire time since we need to disable them at * least once for the stats anyway. */ rcu_read_lock_bh(); - hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnode) { + hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) { if (nf_ct_tuplehash_to_ctrack(h) != ignored_conntrack && nf_ct_tuple_equal(tuple, &h->tuple)) { NF_CT_STAT_INC(net, found); @@ -430,14 +453,14 @@ static noinline int early_drop(struct net *net, unsigned int hash) /* Use oldest entry, which is roughly LRU */ struct nf_conntrack_tuple_hash *h; struct nf_conn *ct = NULL, *tmp; - struct hlist_node *n; + struct hlist_nulls_node *n; unsigned int i, cnt = 0; int dropped = 0; rcu_read_lock(); for (i = 0; i < nf_conntrack_htable_size; i++) { - hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash], - hnode) { + hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], + hnnode) { tmp = nf_ct_tuplehash_to_ctrack(h); if (!test_bit(IPS_ASSURED_BIT, &tmp->status)) ct = tmp; @@ -508,27 +531,19 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, #ifdef CONFIG_NET_NS ct->ct_net = net; #endif - INIT_RCU_HEAD(&ct->rcu); return ct; } EXPORT_SYMBOL_GPL(nf_conntrack_alloc); -static void nf_conntrack_free_rcu(struct rcu_head *head) -{ - struct nf_conn *ct = container_of(head, struct nf_conn, rcu); - - nf_ct_ext_free(ct); - kmem_cache_free(nf_conntrack_cachep, ct); -} - void nf_conntrack_free(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); nf_ct_ext_destroy(ct); atomic_dec(&net->ct.count); - call_rcu(&ct->rcu, nf_conntrack_free_rcu); + nf_ct_ext_free(ct); + kmem_cache_free(nf_conntrack_cachep, ct); } EXPORT_SYMBOL_GPL(nf_conntrack_free); @@ -594,7 +609,7 @@ init_conntrack(struct net *net, } /* Overload tuple linked list to put us in unconfirmed list. */ - hlist_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode, + hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, &net->ct.unconfirmed); spin_unlock_bh(&nf_conntrack_lock); @@ -934,17 +949,17 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), { struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; - struct hlist_node *n; + struct hlist_nulls_node *n; spin_lock_bh(&nf_conntrack_lock); for (; *bucket < nf_conntrack_htable_size; (*bucket)++) { - hlist_for_each_entry(h, n, &net->ct.hash[*bucket], hnode) { + hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) { ct = nf_ct_tuplehash_to_ctrack(h); if (iter(ct, data)) goto found; } } - hlist_for_each_entry(h, n, &net->ct.unconfirmed, hnode) { + hlist_nulls_for_each_entry(h, n, &net->ct.unconfirmed, hnnode) { ct = nf_ct_tuplehash_to_ctrack(h); if (iter(ct, data)) set_bit(IPS_DYING_BIT, &ct->status); @@ -992,7 +1007,7 @@ static int kill_all(struct nf_conn *i, void *data) return 1; } -void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced, unsigned int size) +void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size) { if (vmalloced) vfree(hash); @@ -1060,26 +1075,28 @@ void nf_conntrack_cleanup(struct net *net) } } -struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced) +void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls) { - struct hlist_head *hash; - unsigned int size, i; + struct hlist_nulls_head *hash; + unsigned int nr_slots, i; + size_t sz; *vmalloced = 0; - size = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_head)); - hash = (void*)__get_free_pages(GFP_KERNEL|__GFP_NOWARN, - get_order(sizeof(struct hlist_head) - * size)); + BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head)); + nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head)); + sz = nr_slots * sizeof(struct hlist_nulls_head); + hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, + get_order(sz)); if (!hash) { *vmalloced = 1; printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n"); - hash = vmalloc(sizeof(struct hlist_head) * size); + hash = __vmalloc(sz, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL); } - if (hash) - for (i = 0; i < size; i++) - INIT_HLIST_HEAD(&hash[i]); + if (hash && nulls) + for (i = 0; i < nr_slots; i++) + INIT_HLIST_NULLS_HEAD(&hash[i], i); return hash; } @@ -1090,7 +1107,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) int i, bucket, vmalloced, old_vmalloced; unsigned int hashsize, old_size; int rnd; - struct hlist_head *hash, *old_hash; + struct hlist_nulls_head *hash, *old_hash; struct nf_conntrack_tuple_hash *h; /* On boot, we can set this without any fancy locking. */ @@ -1101,7 +1118,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) if (!hashsize) return -EINVAL; - hash = nf_ct_alloc_hashtable(&hashsize, &vmalloced); + hash = nf_ct_alloc_hashtable(&hashsize, &vmalloced, 1); if (!hash) return -ENOMEM; @@ -1116,12 +1133,12 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) */ spin_lock_bh(&nf_conntrack_lock); for (i = 0; i < nf_conntrack_htable_size; i++) { - while (!hlist_empty(&init_net.ct.hash[i])) { - h = hlist_entry(init_net.ct.hash[i].first, - struct nf_conntrack_tuple_hash, hnode); - hlist_del_rcu(&h->hnode); + while (!hlist_nulls_empty(&init_net.ct.hash[i])) { + h = hlist_nulls_entry(init_net.ct.hash[i].first, + struct nf_conntrack_tuple_hash, hnnode); + hlist_nulls_del_rcu(&h->hnnode); bucket = __hash_conntrack(&h->tuple, hashsize, rnd); - hlist_add_head_rcu(&h->hnode, &hash[bucket]); + hlist_nulls_add_head_rcu(&h->hnnode, &hash[bucket]); } } old_size = nf_conntrack_htable_size; @@ -1172,7 +1189,7 @@ static int nf_conntrack_init_init_net(void) nf_conntrack_cachep = kmem_cache_create("nf_conntrack", sizeof(struct nf_conn), - 0, 0, NULL); + 0, SLAB_DESTROY_BY_RCU, NULL); if (!nf_conntrack_cachep) { printk(KERN_ERR "Unable to create nf_conn slab cache\n"); ret = -ENOMEM; @@ -1202,7 +1219,7 @@ static int nf_conntrack_init_net(struct net *net) int ret; atomic_set(&net->ct.count, 0); - INIT_HLIST_HEAD(&net->ct.unconfirmed); + INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, 0); net->ct.stat = alloc_percpu(struct ip_conntrack_stat); if (!net->ct.stat) { ret = -ENOMEM; @@ -1212,7 +1229,7 @@ static int nf_conntrack_init_net(struct net *net) if (ret < 0) goto err_ecache; net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, - &net->ct.hash_vmalloc); + &net->ct.hash_vmalloc, 1); if (!net->ct.hash) { ret = -ENOMEM; printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 357ba39d4c8..3940f996a2e 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -604,7 +604,7 @@ int nf_conntrack_expect_init(struct net *net) net->ct.expect_count = 0; net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, - &net->ct.expect_vmalloc); + &net->ct.expect_vmalloc, 0); if (net->ct.expect_hash == NULL) goto err1; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 805cfdd4230..30b8e9009f9 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -159,6 +159,7 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, struct nf_conntrack_tuple_hash *h; struct nf_conntrack_expect *exp; const struct hlist_node *n, *next; + const struct hlist_nulls_node *nn; unsigned int i; /* Get rid of expectations */ @@ -175,10 +176,10 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, } /* Get rid of expecteds, set helpers to NULL. */ - hlist_for_each_entry(h, n, &net->ct.unconfirmed, hnode) + hlist_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode) unhelp(h, me); for (i = 0; i < nf_conntrack_htable_size; i++) { - hlist_for_each_entry(h, n, &net->ct.hash[i], hnode) + hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) unhelp(h, me); } } @@ -218,7 +219,7 @@ int nf_conntrack_helper_init(void) nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, - &nf_ct_helper_vmalloc); + &nf_ct_helper_vmalloc, 0); if (!nf_ct_helper_hash) return -ENOMEM; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 1b75c9efb0e..349bbefe551 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -536,7 +537,7 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { struct nf_conn *ct, *last; struct nf_conntrack_tuple_hash *h; - struct hlist_node *n; + struct hlist_nulls_node *n; struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; @@ -544,27 +545,27 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) last = (struct nf_conn *)cb->args[1]; for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) { restart: - hlist_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]], - hnode) { + hlist_nulls_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]], + hnnode) { if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) continue; ct = nf_ct_tuplehash_to_ctrack(h); + if (!atomic_inc_not_zero(&ct->ct_general.use)) + continue; /* Dump entries of a given L3 protocol number. * If it is not specified, ie. l3proto == 0, * then dump everything. */ if (l3proto && nf_ct_l3num(ct) != l3proto) - continue; + goto releasect; if (cb->args[1]) { if (ct != last) - continue; + goto releasect; cb->args[1] = 0; } if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, 1, ct) < 0) { - if (!atomic_inc_not_zero(&ct->ct_general.use)) - continue; cb->args[1] = (unsigned long)ct; goto out; } @@ -577,6 +578,8 @@ restart: if (acct) memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX])); } +releasect: + nf_ct_put(ct); } if (cb->args[1]) { cb->args[1] = 0; @@ -1242,13 +1245,12 @@ ctnetlink_create_conntrack(struct nlattr *cda[], if (err < 0) goto err2; - master_h = __nf_conntrack_find(&init_net, &master); + master_h = nf_conntrack_find_get(&init_net, &master); if (master_h == NULL) { err = -ENOENT; goto err2; } master_ct = nf_ct_tuplehash_to_ctrack(master_h); - nf_conntrack_get(&master_ct->ct_general); __set_bit(IPS_EXPECTED_BIT, &ct->status); ct->master = master_ct; } diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 4da54b0b923..19351538197 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -44,40 +44,42 @@ struct ct_iter_state { unsigned int bucket; }; -static struct hlist_node *ct_get_first(struct seq_file *seq) +static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) { struct net *net = seq_file_net(seq); struct ct_iter_state *st = seq->private; - struct hlist_node *n; + struct hlist_nulls_node *n; for (st->bucket = 0; st->bucket < nf_conntrack_htable_size; st->bucket++) { n = rcu_dereference(net->ct.hash[st->bucket].first); - if (n) + if (!is_a_nulls(n)) return n; } return NULL; } -static struct hlist_node *ct_get_next(struct seq_file *seq, - struct hlist_node *head) +static struct hlist_nulls_node *ct_get_next(struct seq_file *seq, + struct hlist_nulls_node *head) { struct net *net = seq_file_net(seq); struct ct_iter_state *st = seq->private; head = rcu_dereference(head->next); - while (head == NULL) { - if (++st->bucket >= nf_conntrack_htable_size) - return NULL; + while (is_a_nulls(head)) { + if (likely(get_nulls_value(head) == st->bucket)) { + if (++st->bucket >= nf_conntrack_htable_size) + return NULL; + } head = rcu_dereference(net->ct.hash[st->bucket].first); } return head; } -static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos) +static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos) { - struct hlist_node *head = ct_get_first(seq); + struct hlist_nulls_node *head = ct_get_first(seq); if (head) while (pos && (head = ct_get_next(seq, head))) @@ -107,67 +109,74 @@ static void ct_seq_stop(struct seq_file *s, void *v) /* return 0 on success, 1 in case of error */ static int ct_seq_show(struct seq_file *s, void *v) { - const struct nf_conntrack_tuple_hash *hash = v; - const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); + struct nf_conntrack_tuple_hash *hash = v; + struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash); const struct nf_conntrack_l3proto *l3proto; const struct nf_conntrack_l4proto *l4proto; + int ret = 0; NF_CT_ASSERT(ct); + if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) + return 0; /* we only want to print DIR_ORIGINAL */ if (NF_CT_DIRECTION(hash)) - return 0; + goto release; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); NF_CT_ASSERT(l3proto); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); NF_CT_ASSERT(l4proto); + ret = -ENOSPC; if (seq_printf(s, "%-8s %u %-8s %u %ld ", l3proto->name, nf_ct_l3num(ct), l4proto->name, nf_ct_protonum(ct), timer_pending(&ct->timeout) ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0) - return -ENOSPC; + goto release; if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct)) - return -ENOSPC; + goto release; if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, l3proto, l4proto)) - return -ENOSPC; + goto release; if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) - return -ENOSPC; + goto release; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) if (seq_printf(s, "[UNREPLIED] ")) - return -ENOSPC; + goto release; if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, l3proto, l4proto)) - return -ENOSPC; + goto release; if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) - return -ENOSPC; + goto release; if (test_bit(IPS_ASSURED_BIT, &ct->status)) if (seq_printf(s, "[ASSURED] ")) - return -ENOSPC; + goto release; #if defined(CONFIG_NF_CONNTRACK_MARK) if (seq_printf(s, "mark=%u ", ct->mark)) - return -ENOSPC; + goto release; #endif #ifdef CONFIG_NF_CONNTRACK_SECMARK if (seq_printf(s, "secmark=%u ", ct->secmark)) - return -ENOSPC; + goto release; #endif if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) - return -ENOSPC; + goto release; + ret = 0; +release: + nf_ct_put(ct); return 0; } diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 7f404cc64c8..68098095439 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -108,7 +108,7 @@ static int count_them(struct xt_connlimit_data *data, const struct nf_conntrack_tuple_hash *found; struct xt_connlimit_conn *conn; struct xt_connlimit_conn *tmp; - const struct nf_conn *found_ct; + struct nf_conn *found_ct; struct list_head *hash; bool addit = true; int matches = 0; @@ -123,7 +123,7 @@ static int count_them(struct xt_connlimit_data *data, /* check the saved connections */ list_for_each_entry_safe(conn, tmp, hash, list) { - found = __nf_conntrack_find(&init_net, &conn->tuple); + found = nf_conntrack_find_get(&init_net, &conn->tuple); found_ct = NULL; if (found != NULL) @@ -151,6 +151,7 @@ static int count_them(struct xt_connlimit_data *data, * we do not care about connections which are * closed already -> ditch it */ + nf_ct_put(found_ct); list_del(&conn->list); kfree(conn); continue; @@ -160,6 +161,7 @@ static int count_them(struct xt_connlimit_data *data, match->family)) /* same source network -> be counted! */ ++matches; + nf_ct_put(found_ct); } rcu_read_unlock(); -- cgit v1.2.3-70-g09d2 From 5c0de29d06318ec8f6e3ba0d17d62529dbbdc1e8 Mon Sep 17 00:00:00 2001 From: Holger Eitzenberger Date: Wed, 25 Mar 2009 21:52:17 +0100 Subject: netfilter: nf_conntrack: add generic function to get len of generic policy Usefull for all protocols which do not add additional data, such as GRE or UDPlite. Signed-off-by: Holger Eitzenberger Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_l4proto.h | 1 + net/netfilter/nf_conntrack_core.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index a120990b3b2..ba32ed7bdab 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -113,6 +113,7 @@ extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple); extern int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], struct nf_conntrack_tuple *t); +extern int nf_ct_port_nlattr_tuple_size(void); extern const struct nla_policy nf_ct_port_nla_policy[]; #ifdef CONFIG_SYSCTL diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index c55bbdc7d42..b182b30c7d8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -921,6 +921,12 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], return 0; } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple); + +int nf_ct_port_nlattr_tuple_size(void) +{ + return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); +} +EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); #endif /* Used by ipt_REJECT and ip6t_REJECT. */ -- cgit v1.2.3-70-g09d2 From a1702857724fb39cb68ce581490010df99168fd0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2009 00:12:24 -0700 Subject: net: Add support for the OpenCores 10/100 Mbps Ethernet MAC. This patch adds a platform device driver that supports the OpenCores 10/100 Mbps Ethernet MAC. The driver expects three resources: one IORESOURCE_MEM resource defines the memory region for the core's memory-mapped registers while a second IORESOURCE_MEM resource defines the network packet buffer space. The third resource, of type IORESOURCE_IRQ, associates an interrupt with the driver. Signed-off-by: Thierry Reding Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/Kconfig | 8 + drivers/net/Makefile | 1 + drivers/net/ethoc.c | 1112 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/net/ethoc.h | 22 + 4 files changed, 1143 insertions(+) create mode 100644 drivers/net/ethoc.c create mode 100644 include/net/ethoc.h (limited to 'include/net') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e5ffc1c606c..f062b424704 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -972,6 +972,14 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ETHOC + tristate "OpenCores 10/100 Mbps Ethernet MAC support" + depends on NET_ETHERNET + select MII + select PHYLIB + help + Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC. + config SMC911X tristate "SMSC LAN911[5678] support" select CRC32 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 758ecdf4c82..98409c9dd44 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -230,6 +230,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o obj-$(CONFIG_MLX4_CORE) += mlx4/ obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c new file mode 100644 index 00000000000..91a9b1a3376 --- /dev/null +++ b/drivers/net/ethoc.c @@ -0,0 +1,1112 @@ +/* + * linux/drivers/net/ethoc.c + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding + */ + +#include +#include +#include +#include +#include +#include +#include + +/* register offsets */ +#define MODER 0x00 +#define INT_SOURCE 0x04 +#define INT_MASK 0x08 +#define IPGT 0x0c +#define IPGR1 0x10 +#define IPGR2 0x14 +#define PACKETLEN 0x18 +#define COLLCONF 0x1c +#define TX_BD_NUM 0x20 +#define CTRLMODER 0x24 +#define MIIMODER 0x28 +#define MIICOMMAND 0x2c +#define MIIADDRESS 0x30 +#define MIITX_DATA 0x34 +#define MIIRX_DATA 0x38 +#define MIISTATUS 0x3c +#define MAC_ADDR0 0x40 +#define MAC_ADDR1 0x44 +#define ETH_HASH0 0x48 +#define ETH_HASH1 0x4c +#define ETH_TXCTRL 0x50 + +/* mode register */ +#define MODER_RXEN (1 << 0) /* receive enable */ +#define MODER_TXEN (1 << 1) /* transmit enable */ +#define MODER_NOPRE (1 << 2) /* no preamble */ +#define MODER_BRO (1 << 3) /* broadcast address */ +#define MODER_IAM (1 << 4) /* individual address mode */ +#define MODER_PRO (1 << 5) /* promiscuous mode */ +#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */ +#define MODER_LOOP (1 << 7) /* loopback */ +#define MODER_NBO (1 << 8) /* no back-off */ +#define MODER_EDE (1 << 9) /* excess defer enable */ +#define MODER_FULLD (1 << 10) /* full duplex */ +#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */ +#define MODER_DCRC (1 << 12) /* delayed CRC enable */ +#define MODER_CRC (1 << 13) /* CRC enable */ +#define MODER_HUGE (1 << 14) /* huge packets enable */ +#define MODER_PAD (1 << 15) /* padding enabled */ +#define MODER_RSM (1 << 16) /* receive small packets */ + +/* interrupt source and mask registers */ +#define INT_MASK_TXF (1 << 0) /* transmit frame */ +#define INT_MASK_TXE (1 << 1) /* transmit error */ +#define INT_MASK_RXF (1 << 2) /* receive frame */ +#define INT_MASK_RXE (1 << 3) /* receive error */ +#define INT_MASK_BUSY (1 << 4) +#define INT_MASK_TXC (1 << 5) /* transmit control frame */ +#define INT_MASK_RXC (1 << 6) /* receive control frame */ + +#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE) +#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE) + +#define INT_MASK_ALL ( \ + INT_MASK_TXF | INT_MASK_TXE | \ + INT_MASK_RXF | INT_MASK_RXE | \ + INT_MASK_TXC | INT_MASK_RXC | \ + INT_MASK_BUSY \ + ) + +/* packet length register */ +#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16) +#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0) +#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \ + PACKETLEN_MAX(max)) + +/* transmit buffer number register */ +#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80) + +/* control module mode register */ +#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */ +#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */ +#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */ + +/* MII mode register */ +#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */ +#define MIIMODER_NOPRE (1 << 8) /* no preamble */ + +/* MII command register */ +#define MIICOMMAND_SCAN (1 << 0) /* scan status */ +#define MIICOMMAND_READ (1 << 1) /* read status */ +#define MIICOMMAND_WRITE (1 << 2) /* write control data */ + +/* MII address register */ +#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0) +#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8) +#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \ + MIIADDRESS_RGAD(reg)) + +/* MII transmit data register */ +#define MIITX_DATA_VAL(x) ((x) & 0xffff) + +/* MII receive data register */ +#define MIIRX_DATA_VAL(x) ((x) & 0xffff) + +/* MII status register */ +#define MIISTATUS_LINKFAIL (1 << 0) +#define MIISTATUS_BUSY (1 << 1) +#define MIISTATUS_INVALID (1 << 2) + +/* TX buffer descriptor */ +#define TX_BD_CS (1 << 0) /* carrier sense lost */ +#define TX_BD_DF (1 << 1) /* defer indication */ +#define TX_BD_LC (1 << 2) /* late collision */ +#define TX_BD_RL (1 << 3) /* retransmission limit */ +#define TX_BD_RETRY_MASK (0x00f0) +#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4) +#define TX_BD_UR (1 << 8) /* transmitter underrun */ +#define TX_BD_CRC (1 << 11) /* TX CRC enable */ +#define TX_BD_PAD (1 << 12) /* pad enable for short packets */ +#define TX_BD_WRAP (1 << 13) +#define TX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define TX_BD_READY (1 << 15) /* TX buffer ready */ +#define TX_BD_LEN(x) (((x) & 0xffff) << 16) +#define TX_BD_LEN_MASK (0xffff << 16) + +#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \ + TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR) + +/* RX buffer descriptor */ +#define RX_BD_LC (1 << 0) /* late collision */ +#define RX_BD_CRC (1 << 1) /* RX CRC error */ +#define RX_BD_SF (1 << 2) /* short frame */ +#define RX_BD_TL (1 << 3) /* too long */ +#define RX_BD_DN (1 << 4) /* dribble nibble */ +#define RX_BD_IS (1 << 5) /* invalid symbol */ +#define RX_BD_OR (1 << 6) /* receiver overrun */ +#define RX_BD_MISS (1 << 7) +#define RX_BD_CF (1 << 8) /* control frame */ +#define RX_BD_WRAP (1 << 13) +#define RX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define RX_BD_EMPTY (1 << 15) +#define RX_BD_LEN(x) (((x) & 0xffff) << 16) + +#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \ + RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS) + +#define ETHOC_BUFSIZ 1536 +#define ETHOC_ZLEN 64 +#define ETHOC_BD_BASE 0x400 +#define ETHOC_TIMEOUT (HZ / 2) +#define ETHOC_MII_TIMEOUT (1 + (HZ / 5)) + +/** + * struct ethoc - driver-private device structure + * @iobase: pointer to I/O memory region + * @membase: pointer to buffer memory region + * @num_tx: number of send buffers + * @cur_tx: last send buffer written + * @dty_tx: last buffer actually sent + * @num_rx: number of receive buffers + * @cur_rx: current receive buffer + * @netdev: pointer to network device structure + * @napi: NAPI structure + * @stats: network device statistics + * @msg_enable: device state flags + * @rx_lock: receive lock + * @lock: device lock + * @phy: attached PHY + * @mdio: MDIO bus for PHY access + * @phy_id: address of attached PHY + */ +struct ethoc { + void __iomem *iobase; + void __iomem *membase; + + unsigned int num_tx; + unsigned int cur_tx; + unsigned int dty_tx; + + unsigned int num_rx; + unsigned int cur_rx; + + struct net_device *netdev; + struct napi_struct napi; + struct net_device_stats stats; + u32 msg_enable; + + spinlock_t rx_lock; + spinlock_t lock; + + struct phy_device *phy; + struct mii_bus *mdio; + s8 phy_id; +}; + +/** + * struct ethoc_bd - buffer descriptor + * @stat: buffer statistics + * @addr: physical memory address + */ +struct ethoc_bd { + u32 stat; + u32 addr; +}; + +static u32 ethoc_read(struct ethoc *dev, loff_t offset) +{ + return ioread32(dev->iobase + offset); +} + +static void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) +{ + iowrite32(data, dev->iobase + offset); +} + +static void ethoc_read_bd(struct ethoc *dev, int index, struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + bd->stat = ethoc_read(dev, offset + 0); + bd->addr = ethoc_read(dev, offset + 4); +} + +static void ethoc_write_bd(struct ethoc *dev, int index, + const struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + ethoc_write(dev, offset + 0, bd->stat); + ethoc_write(dev, offset + 4, bd->addr); +} + +static void ethoc_enable_irq(struct ethoc *dev, u32 mask) +{ + u32 imask = ethoc_read(dev, INT_MASK); + imask |= mask; + ethoc_write(dev, INT_MASK, imask); +} + +static void ethoc_disable_irq(struct ethoc *dev, u32 mask) +{ + u32 imask = ethoc_read(dev, INT_MASK); + imask &= ~mask; + ethoc_write(dev, INT_MASK, imask); +} + +static void ethoc_ack_irq(struct ethoc *dev, u32 mask) +{ + ethoc_write(dev, INT_SOURCE, mask); +} + +static void ethoc_enable_rx_and_tx(struct ethoc *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode |= MODER_RXEN | MODER_TXEN; + ethoc_write(dev, MODER, mode); +} + +static void ethoc_disable_rx_and_tx(struct ethoc *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode &= ~(MODER_RXEN | MODER_TXEN); + ethoc_write(dev, MODER, mode); +} + +static int ethoc_init_ring(struct ethoc *dev) +{ + struct ethoc_bd bd; + int i; + + dev->cur_tx = 0; + dev->dty_tx = 0; + dev->cur_rx = 0; + + /* setup transmission buffers */ + bd.addr = 0; + bd.stat = TX_BD_IRQ | TX_BD_CRC; + + for (i = 0; i < dev->num_tx; i++) { + if (i == dev->num_tx - 1) + bd.stat |= TX_BD_WRAP; + + ethoc_write_bd(dev, i, &bd); + bd.addr += ETHOC_BUFSIZ; + } + + bd.addr = dev->num_tx * ETHOC_BUFSIZ; + bd.stat = RX_BD_EMPTY | RX_BD_IRQ; + + for (i = 0; i < dev->num_rx; i++) { + if (i == dev->num_rx - 1) + bd.stat |= RX_BD_WRAP; + + ethoc_write_bd(dev, dev->num_tx + i, &bd); + bd.addr += ETHOC_BUFSIZ; + } + + return 0; +} + +static int ethoc_reset(struct ethoc *dev) +{ + u32 mode; + + /* TODO: reset controller? */ + + ethoc_disable_rx_and_tx(dev); + + /* TODO: setup registers */ + + /* enable FCS generation and automatic padding */ + mode = ethoc_read(dev, MODER); + mode |= MODER_CRC | MODER_PAD; + ethoc_write(dev, MODER, mode); + + /* set full-duplex mode */ + mode = ethoc_read(dev, MODER); + mode |= MODER_FULLD; + ethoc_write(dev, MODER, mode); + ethoc_write(dev, IPGT, 0x15); + + ethoc_ack_irq(dev, INT_MASK_ALL); + ethoc_enable_irq(dev, INT_MASK_ALL); + ethoc_enable_rx_and_tx(dev); + return 0; +} + +static unsigned int ethoc_update_rx_stats(struct ethoc *dev, + struct ethoc_bd *bd) +{ + struct net_device *netdev = dev->netdev; + unsigned int ret = 0; + + if (bd->stat & RX_BD_TL) { + dev_err(&netdev->dev, "RX: frame too long\n"); + dev->stats.rx_length_errors++; + ret++; + } + + if (bd->stat & RX_BD_SF) { + dev_err(&netdev->dev, "RX: frame too short\n"); + dev->stats.rx_length_errors++; + ret++; + } + + if (bd->stat & RX_BD_DN) { + dev_err(&netdev->dev, "RX: dribble nibble\n"); + dev->stats.rx_frame_errors++; + } + + if (bd->stat & RX_BD_CRC) { + dev_err(&netdev->dev, "RX: wrong CRC\n"); + dev->stats.rx_crc_errors++; + ret++; + } + + if (bd->stat & RX_BD_OR) { + dev_err(&netdev->dev, "RX: overrun\n"); + dev->stats.rx_over_errors++; + ret++; + } + + if (bd->stat & RX_BD_MISS) + dev->stats.rx_missed_errors++; + + if (bd->stat & RX_BD_LC) { + dev_err(&netdev->dev, "RX: late collision\n"); + dev->stats.collisions++; + ret++; + } + + return ret; +} + +static int ethoc_rx(struct net_device *dev, int limit) +{ + struct ethoc *priv = netdev_priv(dev); + int count; + + for (count = 0; count < limit; ++count) { + unsigned int entry; + struct ethoc_bd bd; + + entry = priv->num_tx + (priv->cur_rx % priv->num_rx); + ethoc_read_bd(priv, entry, &bd); + if (bd.stat & RX_BD_EMPTY) + break; + + if (ethoc_update_rx_stats(priv, &bd) == 0) { + int size = bd.stat >> 16; + struct sk_buff *skb = netdev_alloc_skb(dev, size); + if (likely(skb)) { + void *src = priv->membase + bd.addr; + memcpy_fromio(skb_put(skb, size), src, size); + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += size; + netif_receive_skb(skb); + } else { + if (net_ratelimit()) + dev_warn(&dev->dev, "low on memory - " + "packet dropped\n"); + + priv->stats.rx_dropped++; + break; + } + } + + /* clear the buffer descriptor so it can be reused */ + bd.stat &= ~RX_BD_STATS; + bd.stat |= RX_BD_EMPTY; + ethoc_write_bd(priv, entry, &bd); + priv->cur_rx++; + } + + return count; +} + +static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd) +{ + struct net_device *netdev = dev->netdev; + + if (bd->stat & TX_BD_LC) { + dev_err(&netdev->dev, "TX: late collision\n"); + dev->stats.tx_window_errors++; + } + + if (bd->stat & TX_BD_RL) { + dev_err(&netdev->dev, "TX: retransmit limit\n"); + dev->stats.tx_aborted_errors++; + } + + if (bd->stat & TX_BD_UR) { + dev_err(&netdev->dev, "TX: underrun\n"); + dev->stats.tx_fifo_errors++; + } + + if (bd->stat & TX_BD_CS) { + dev_err(&netdev->dev, "TX: carrier sense lost\n"); + dev->stats.tx_carrier_errors++; + } + + if (bd->stat & TX_BD_STATS) + dev->stats.tx_errors++; + + dev->stats.collisions += (bd->stat >> 4) & 0xf; + dev->stats.tx_bytes += bd->stat >> 16; + dev->stats.tx_packets++; + return 0; +} + +static void ethoc_tx(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + + while (priv->dty_tx != priv->cur_tx) { + unsigned int entry = priv->dty_tx % priv->num_tx; + struct ethoc_bd bd; + + ethoc_read_bd(priv, entry, &bd); + if (bd.stat & TX_BD_READY) + break; + + entry = (++priv->dty_tx) % priv->num_tx; + (void)ethoc_update_tx_stats(priv, &bd); + } + + if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2)) + netif_wake_queue(dev); + + ethoc_ack_irq(priv, INT_MASK_TX); + spin_unlock(&priv->lock); +} + +static irqreturn_t ethoc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct ethoc *priv = netdev_priv(dev); + u32 pending; + + ethoc_disable_irq(priv, INT_MASK_ALL); + pending = ethoc_read(priv, INT_SOURCE); + if (unlikely(pending == 0)) { + ethoc_enable_irq(priv, INT_MASK_ALL); + return IRQ_NONE; + } + + ethoc_ack_irq(priv, INT_MASK_ALL); + + if (pending & INT_MASK_BUSY) { + dev_err(&dev->dev, "packet dropped\n"); + priv->stats.rx_dropped++; + } + + if (pending & INT_MASK_RX) { + if (napi_schedule_prep(&priv->napi)) + __napi_schedule(&priv->napi); + } else { + ethoc_enable_irq(priv, INT_MASK_RX); + } + + if (pending & INT_MASK_TX) + ethoc_tx(dev); + + ethoc_enable_irq(priv, INT_MASK_ALL & ~INT_MASK_RX); + return IRQ_HANDLED; +} + +static int ethoc_get_mac_address(struct net_device *dev, void *addr) +{ + struct ethoc *priv = netdev_priv(dev); + u8 *mac = (u8 *)addr; + u32 reg; + + reg = ethoc_read(priv, MAC_ADDR0); + mac[2] = (reg >> 24) & 0xff; + mac[3] = (reg >> 16) & 0xff; + mac[4] = (reg >> 8) & 0xff; + mac[5] = (reg >> 0) & 0xff; + + reg = ethoc_read(priv, MAC_ADDR1); + mac[0] = (reg >> 8) & 0xff; + mac[1] = (reg >> 0) & 0xff; + + return 0; +} + +static int ethoc_poll(struct napi_struct *napi, int budget) +{ + struct ethoc *priv = container_of(napi, struct ethoc, napi); + int work_done = 0; + + work_done = ethoc_rx(priv->netdev, budget); + if (work_done < budget) { + ethoc_enable_irq(priv, INT_MASK_RX); + napi_complete(napi); + } + + return work_done; +} + +static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg) +{ + unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT; + struct ethoc *priv = bus->priv; + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg)); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ); + + while (time_before(jiffies, timeout)) { + u32 status = ethoc_read(priv, MIISTATUS); + if (!(status & MIISTATUS_BUSY)) { + u32 data = ethoc_read(priv, MIIRX_DATA); + /* reset MII command register */ + ethoc_write(priv, MIICOMMAND, 0); + return data; + } + + schedule(); + } + + return -EBUSY; +} + +static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT; + struct ethoc *priv = bus->priv; + + ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg)); + ethoc_write(priv, MIITX_DATA, val); + ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE); + + while (time_before(jiffies, timeout)) { + u32 stat = ethoc_read(priv, MIISTATUS); + if (!(stat & MIISTATUS_BUSY)) + return 0; + + schedule(); + } + + return -EBUSY; +} + +static int ethoc_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +static void ethoc_mdio_poll(struct net_device *dev) +{ +} + +static int ethoc_mdio_probe(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + struct phy_device *phy; + int i; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + phy = priv->mdio->phy_map[i]; + if (phy) { + if (priv->phy_id != -1) { + /* attach to specified PHY */ + if (priv->phy_id == phy->addr) + break; + } else { + /* autoselect PHY if none was specified */ + if (phy->addr != 0) + break; + } + } + } + + if (!phy) { + dev_err(&dev->dev, "no PHY found\n"); + return -ENXIO; + } + + phy = phy_connect(dev, dev_name(&phy->dev), ðoc_mdio_poll, 0, + PHY_INTERFACE_MODE_GMII); + if (IS_ERR(phy)) { + dev_err(&dev->dev, "could not attach to PHY\n"); + return PTR_ERR(phy); + } + + priv->phy = phy; + return 0; +} + +static int ethoc_open(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + unsigned int min_tx = 2; + unsigned int num_bd; + int ret; + + ret = request_irq(dev->irq, ethoc_interrupt, IRQF_SHARED, + dev->name, dev); + if (ret) + return ret; + + /* calculate the number of TX/RX buffers */ + num_bd = (dev->mem_end - dev->mem_start + 1) / ETHOC_BUFSIZ; + priv->num_tx = min(min_tx, num_bd / 4); + priv->num_rx = num_bd - priv->num_tx; + ethoc_write(priv, TX_BD_NUM, priv->num_tx); + + ethoc_init_ring(priv); + ethoc_reset(priv); + + if (netif_queue_stopped(dev)) { + dev_dbg(&dev->dev, " resuming queue\n"); + netif_wake_queue(dev); + } else { + dev_dbg(&dev->dev, " starting queue\n"); + netif_start_queue(dev); + } + + phy_start(priv->phy); + napi_enable(&priv->napi); + + if (netif_msg_ifup(priv)) { + dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n", + dev->base_addr, dev->mem_start, dev->mem_end); + } + + return 0; +} + +static int ethoc_stop(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + + napi_disable(&priv->napi); + + if (priv->phy) + phy_stop(priv->phy); + + ethoc_disable_rx_and_tx(priv); + free_irq(dev->irq, dev); + + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + return 0; +} + +static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ethoc *priv = netdev_priv(dev); + struct mii_ioctl_data *mdio = if_mii(ifr); + struct phy_device *phy = NULL; + + if (!netif_running(dev)) + return -EINVAL; + + if (cmd != SIOCGMIIPHY) { + if (mdio->phy_id >= PHY_MAX_ADDR) + return -ERANGE; + + phy = priv->mdio->phy_map[mdio->phy_id]; + if (!phy) + return -ENODEV; + } else { + phy = priv->phy; + } + + return phy_mii_ioctl(phy, mdio, cmd); +} + +static int ethoc_config(struct net_device *dev, struct ifmap *map) +{ + return -ENOSYS; +} + +static int ethoc_set_mac_address(struct net_device *dev, void *addr) +{ + struct ethoc *priv = netdev_priv(dev); + u8 *mac = (u8 *)addr; + + ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | + (mac[4] << 8) | (mac[5] << 0)); + ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); + + return 0; +} + +static void ethoc_set_multicast_list(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + u32 mode = ethoc_read(priv, MODER); + struct dev_mc_list *mc = NULL; + u32 hash[2] = { 0, 0 }; + + /* set loopback mode if requested */ + if (dev->flags & IFF_LOOPBACK) + mode |= MODER_LOOP; + else + mode &= ~MODER_LOOP; + + /* receive broadcast frames if requested */ + if (dev->flags & IFF_BROADCAST) + mode &= ~MODER_BRO; + else + mode |= MODER_BRO; + + /* enable promiscuous mode if requested */ + if (dev->flags & IFF_PROMISC) + mode |= MODER_PRO; + else + mode &= ~MODER_PRO; + + ethoc_write(priv, MODER, mode); + + /* receive multicast frames */ + if (dev->flags & IFF_ALLMULTI) { + hash[0] = 0xffffffff; + hash[1] = 0xffffffff; + } else { + for (mc = dev->mc_list; mc; mc = mc->next) { + u32 crc = ether_crc(mc->dmi_addrlen, mc->dmi_addr); + int bit = (crc >> 26) & 0x3f; + hash[bit >> 5] |= 1 << (bit & 0x1f); + } + } + + ethoc_write(priv, ETH_HASH0, hash[0]); + ethoc_write(priv, ETH_HASH1, hash[1]); +} + +static int ethoc_change_mtu(struct net_device *dev, int new_mtu) +{ + return -ENOSYS; +} + +static void ethoc_tx_timeout(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + u32 pending = ethoc_read(priv, INT_SOURCE); + if (likely(pending)) + ethoc_interrupt(dev->irq, dev); +} + +static struct net_device_stats *ethoc_stats(struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + return &priv->stats; +} + +static int ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ethoc *priv = netdev_priv(dev); + struct ethoc_bd bd; + unsigned int entry; + void *dest; + + if (unlikely(skb->len > ETHOC_BUFSIZ)) { + priv->stats.tx_errors++; + return -EMSGSIZE; + } + + entry = priv->cur_tx % priv->num_tx; + spin_lock_irq(&priv->lock); + priv->cur_tx++; + + ethoc_read_bd(priv, entry, &bd); + if (unlikely(skb->len < ETHOC_ZLEN)) + bd.stat |= TX_BD_PAD; + else + bd.stat &= ~TX_BD_PAD; + + dest = priv->membase + bd.addr; + memcpy_toio(dest, skb->data, skb->len); + + bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); + bd.stat |= TX_BD_LEN(skb->len); + ethoc_write_bd(priv, entry, &bd); + + bd.stat |= TX_BD_READY; + ethoc_write_bd(priv, entry, &bd); + + if (priv->cur_tx == (priv->dty_tx + priv->num_tx)) { + dev_dbg(&dev->dev, "stopping queue\n"); + netif_stop_queue(dev); + } + + dev->trans_start = jiffies; + dev_kfree_skb(skb); + + spin_unlock_irq(&priv->lock); + return NETDEV_TX_OK; +} + +static const struct net_device_ops ethoc_netdev_ops = { + .ndo_open = ethoc_open, + .ndo_stop = ethoc_stop, + .ndo_do_ioctl = ethoc_ioctl, + .ndo_set_config = ethoc_config, + .ndo_set_mac_address = ethoc_set_mac_address, + .ndo_set_multicast_list = ethoc_set_multicast_list, + .ndo_change_mtu = ethoc_change_mtu, + .ndo_tx_timeout = ethoc_tx_timeout, + .ndo_get_stats = ethoc_stats, + .ndo_start_xmit = ethoc_start_xmit, +}; + +/** + * ethoc_probe() - initialize OpenCores ethernet MAC + * pdev: platform device + */ +static int ethoc_probe(struct platform_device *pdev) +{ + struct net_device *netdev = NULL; + struct resource *res = NULL; + struct resource *mmio = NULL; + struct resource *mem = NULL; + struct ethoc *priv = NULL; + unsigned int phy; + int ret = 0; + + /* allocate networking device */ + netdev = alloc_etherdev(sizeof(struct ethoc)); + if (!netdev) { + dev_err(&pdev->dev, "cannot allocate network device\n"); + ret = -ENOMEM; + goto out; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + platform_set_drvdata(pdev, netdev); + + /* obtain I/O memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory space\n"); + ret = -ENXIO; + goto free; + } + + mmio = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!res) { + dev_err(&pdev->dev, "cannot request I/O memory space\n"); + ret = -ENXIO; + goto free; + } + + netdev->base_addr = mmio->start; + + /* obtain buffer memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "cannot obtain memory space\n"); + ret = -ENXIO; + goto free; + } + + mem = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!mem) { + dev_err(&pdev->dev, "cannot request memory space\n"); + ret = -ENXIO; + goto free; + } + + netdev->mem_start = mem->start; + netdev->mem_end = mem->end; + + /* obtain device IRQ number */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain IRQ\n"); + ret = -ENXIO; + goto free; + } + + netdev->irq = res->start; + + /* setup driver-private data */ + priv = netdev_priv(netdev); + priv->netdev = netdev; + + priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr, + mmio->end - mmio->start + 1); + if (!priv->iobase) { + dev_err(&pdev->dev, "cannot remap I/O memory space\n"); + ret = -ENXIO; + goto error; + } + + priv->membase = devm_ioremap_nocache(&pdev->dev, netdev->mem_start, + mem->end - mem->start + 1); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot remap memory space\n"); + ret = -ENXIO; + goto error; + } + + /* Allow the platform setup code to pass in a MAC address. */ + if (pdev->dev.platform_data) { + struct ethoc_platform_data *pdata = + (struct ethoc_platform_data *)pdev->dev.platform_data; + memcpy(netdev->dev_addr, pdata->hwaddr, IFHWADDRLEN); + priv->phy_id = pdata->phy_id; + } + + /* Check that the given MAC address is valid. If it isn't, read the + * current MAC from the controller. */ + if (!is_valid_ether_addr(netdev->dev_addr)) + ethoc_get_mac_address(netdev, netdev->dev_addr); + + /* Check the MAC again for validity, if it still isn't choose and + * program a random one. */ + if (!is_valid_ether_addr(netdev->dev_addr)) + random_ether_addr(netdev->dev_addr); + + ethoc_set_mac_address(netdev, netdev->dev_addr); + + /* register MII bus */ + priv->mdio = mdiobus_alloc(); + if (!priv->mdio) { + ret = -ENOMEM; + goto free; + } + + priv->mdio->name = "ethoc-mdio"; + snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%d", + priv->mdio->name, pdev->id); + priv->mdio->read = ethoc_mdio_read; + priv->mdio->write = ethoc_mdio_write; + priv->mdio->reset = ethoc_mdio_reset; + priv->mdio->priv = priv; + + priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!priv->mdio->irq) { + ret = -ENOMEM; + goto free_mdio; + } + + for (phy = 0; phy < PHY_MAX_ADDR; phy++) + priv->mdio->irq[phy] = PHY_POLL; + + ret = mdiobus_register(priv->mdio); + if (ret) { + dev_err(&netdev->dev, "failed to register MDIO bus\n"); + goto free_mdio; + } + + ret = ethoc_mdio_probe(netdev); + if (ret) { + dev_err(&netdev->dev, "failed to probe MDIO bus\n"); + goto error; + } + + ether_setup(netdev); + + /* setup the net_device structure */ + netdev->netdev_ops = ðoc_netdev_ops; + netdev->watchdog_timeo = ETHOC_TIMEOUT; + netdev->features |= 0; + + /* setup NAPI */ + memset(&priv->napi, 0, sizeof(priv->napi)); + netif_napi_add(netdev, &priv->napi, ethoc_poll, 64); + + spin_lock_init(&priv->rx_lock); + spin_lock_init(&priv->lock); + + ret = register_netdev(netdev); + if (ret < 0) { + dev_err(&netdev->dev, "failed to register interface\n"); + goto error; + } + + goto out; + +error: + mdiobus_unregister(priv->mdio); +free_mdio: + kfree(priv->mdio->irq); + mdiobus_free(priv->mdio); +free: + free_netdev(netdev); +out: + return ret; +} + +/** + * ethoc_remove() - shutdown OpenCores ethernet MAC + * @pdev: platform device + */ +static int ethoc_remove(struct platform_device *pdev) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct ethoc *priv = netdev_priv(netdev); + + platform_set_drvdata(pdev, NULL); + + if (netdev) { + phy_disconnect(priv->phy); + priv->phy = NULL; + + if (priv->mdio) { + mdiobus_unregister(priv->mdio); + kfree(priv->mdio->irq); + mdiobus_free(priv->mdio); + } + + unregister_netdev(netdev); + free_netdev(netdev); + } + + return 0; +} + +#ifdef CONFIG_PM +static int ethoc_suspend(struct platform_device *pdev, pm_message_t state) +{ + return -ENOSYS; +} + +static int ethoc_resume(struct platform_device *pdev) +{ + return -ENOSYS; +} +#else +# define ethoc_suspend NULL +# define ethoc_resume NULL +#endif + +static struct platform_driver ethoc_driver = { + .probe = ethoc_probe, + .remove = ethoc_remove, + .suspend = ethoc_suspend, + .resume = ethoc_resume, + .driver = { + .name = "ethoc", + }, +}; + +static int __init ethoc_init(void) +{ + return platform_driver_register(ðoc_driver); +} + +static void __exit ethoc_exit(void) +{ + platform_driver_unregister(ðoc_driver); +} + +module_init(ethoc_init); +module_exit(ethoc_exit); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("OpenCores Ethernet MAC driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/include/net/ethoc.h b/include/net/ethoc.h new file mode 100644 index 00000000000..96f3789b27b --- /dev/null +++ b/include/net/ethoc.h @@ -0,0 +1,22 @@ +/* + * linux/include/net/ethoc.h + * + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding + */ + +#ifndef LINUX_NET_ETHOC_H +#define LINUX_NET_ETHOC_H 1 + +struct ethoc_platform_data { + u8 hwaddr[IFHWADDRLEN]; + s8 phy_id; +}; + +#endif /* !LINUX_NET_ETHOC_H */ + -- cgit v1.2.3-70-g09d2 From 176be728ee7d32cfd33702d82c0733e51f66ab5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 23:49:28 +0100 Subject: mac80211: remove ieee80211_num_regular_queues This inline is useless and actually makes the code _longer_ rather than shorter. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ----- net/mac80211/mlme.c | 2 +- net/mac80211/tx.c | 7 +++---- 3 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 12a52efcd0d..3bfc6c6c8c4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1017,11 +1017,6 @@ static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); } -static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw) -{ - return hw->queues; -} - static inline struct ieee80211_rate * ieee80211_get_tx_rate(const struct ieee80211_hw *hw, const struct ieee80211_tx_info *c) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 841b8450b3d..aaf7793583a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1834,7 +1834,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; - if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4) + if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 457238a2f3f..038460b0a48 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1666,8 +1666,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, } /* receiver and we are QoS enabled, use a QoS type frame */ - if (sta_flags & WLAN_STA_WME && - ieee80211_num_regular_queues(&local->hw) >= 4) { + if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1802,7 +1801,7 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) int i, j; struct ieee80211_tx_stored_packet *store; - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { if (!test_bit(i, local->queues_pending)) continue; store = &local->pending_packet[i]; @@ -1827,7 +1826,7 @@ void ieee80211_tx_pending(unsigned long data) int i, ret; netif_tx_lock_bh(dev); - for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) { + for (i = 0; i < local->hw.queues; i++) { /* Check that this queue is ok */ if (__netif_subqueue_stopped(local->mdev, i) && !test_bit(i, local->queues_pending_run)) -- cgit v1.2.3-70-g09d2 From 51b381479ff5bc9b8c49ce15fd8bc35c6b695ca4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2009 11:16:48 +0100 Subject: mac80211: reduce max number of queues No hw/driver actually supports more than four queues right now, and we allocate a number of things per queue which means we waste a bit of memory. Reduce the maximum number to four to accurately reflect what we do (and need for QoS). Even if we had hardware supporting more queues we couldn't take advantage of that right now anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3bfc6c6c8c4..a38a82c785d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -97,7 +97,7 @@ struct ieee80211_ht_bss_info { * for A-MPDU operation. */ enum ieee80211_max_queues { - IEEE80211_MAX_QUEUES = 16, + IEEE80211_MAX_QUEUES = 4, IEEE80211_MAX_AMPDU_QUEUES = 16, }; -- cgit v1.2.3-70-g09d2 From aae89831df03e5282a8f5c0ee46432cfb677fc5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Mar 2009 12:52:10 +0100 Subject: wireless: radiotap updates Radiotap was updated to include a "bad PLCP" flag and standardise the "bad FCS" flag in the "flags" rather than "RX flags" field, this patch updates Linux to that standard. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/radiotap.h | 10 ---------- drivers/net/wireless/libertas/rx.c | 12 ++---------- include/net/ieee80211_radiotap.h | 4 +++- net/mac80211/rx.c | 7 ++++--- 4 files changed, 9 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h index f8eb9097ff0..d16b26416e8 100644 --- a/drivers/net/wireless/libertas/radiotap.h +++ b/drivers/net/wireless/libertas/radiotap.h @@ -33,22 +33,12 @@ struct rx_radiotap_hdr { struct ieee80211_radiotap_header hdr; u8 flags; u8 rate; - u16 chan_freq; - u16 chan_flags; - u8 antenna; u8 antsignal; - u16 rx_flags; -#if 0 - u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; -#endif } __attribute__ ((packed)); #define RX_RADIOTAP_PRESENT ( \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ - (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ 0) diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index 4f60948dde9..63d7e19ce9b 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -351,19 +351,11 @@ static int process_rxed_802_11_packet(struct lbs_private *priv, radiotap_hdr.hdr.it_pad = 0; radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); - /* unknown values */ - radiotap_hdr.flags = 0; - radiotap_hdr.chan_freq = 0; - radiotap_hdr.chan_flags = 0; - radiotap_hdr.antenna = 0; - /* known values */ + if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) + radiotap_hdr.flags |= IEEE80211_RADIOTAP_F_BADFCS; radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); /* XXX must check no carryout */ radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; - radiotap_hdr.rx_flags = 0; - if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) - radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; - //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); /* chop the rxpd */ skb_pull(skb, sizeof(struct rxpd)); diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 384698cb773..23c3f3d9777 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -230,8 +230,10 @@ enum ieee80211_radiotap_type { * 802.11 header and payload * (to 32-bit boundary) */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ + /* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ +#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 66f7ecf51b9..fcc0a599579 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -142,6 +142,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_FLAGS */ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) *pos |= IEEE80211_RADIOTAP_F_FCS; + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) + *pos |= IEEE80211_RADIOTAP_F_BADFCS; if (status->flag & RX_FLAG_SHORTPRE) *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; pos++; @@ -204,9 +206,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* ensure 2 byte alignment for the 2 byte field as required */ if ((pos - (unsigned char *)rthdr) & 1) pos++; - /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */ - if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) - *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS); + if (status->flag & RX_FLAG_FAILED_PLCP_CRC) + *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP); pos += 2; } -- cgit v1.2.3-70-g09d2 From b3a902850a8f5bc11a660051faae707f928d4bd6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Mar 2009 14:29:38 +0100 Subject: mac80211: kill IEEE80211_CONF_SHORT_SLOT_TIME No drivers use it any more, so it can now be removed safely. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a38a82c785d..daa539a4287 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -520,12 +520,6 @@ enum ieee80211_conf_flags { IEEE80211_CONF_PS = (1<<1), }; -/* XXX: remove all this once drivers stop trying to use it */ -static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void) -{ - return 0; -} -#define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME()) /** * enum ieee80211_conf_changed - denotes which configuration changed -- cgit v1.2.3-70-g09d2 From 6039f6d23fe792d615da5449e9fa1c6b43caacf6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:21 +0200 Subject: nl80211: Event notifications for MLME events Add new nl80211 event notifications (and a new multicast group, "mlme") for informing user space about received and processed Authentication, (Re)Association Response, Deauthentication, and Disassociation frames in station and IBSS modes (i.e., MLME SAP interface primitives MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm, MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11 management frame since we already have the frame in that format and it includes all the needed information. This is the initial step in providing MLME SAP interface for authentication and association with nl80211. In other words, kernel code will act as the MLME and a user space application can control it as the SME. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 36 ++++++++++++++++++++++++- include/net/cfg80211.h | 46 +++++++++++++++++++++++++++++++ net/mac80211/mlme.c | 9 +++++-- net/wireless/Makefile | 2 +- net/wireless/mlme.c | 46 +++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 12 +++++++++ 7 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 net/wireless/mlme.c (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 3700d927e24..5ce68ae8314 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -161,6 +161,25 @@ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * + * @NL80211_CMD_AUTHENTICATE: authentication notification (on the "mlme" + * multicast group). This event reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). + * @NL80211_CMD_ASSOCIATE: association notification; like + * NL80211_CMD_AUTHENTICATE but for Association Response and Reassociation + * Response frames (similar to MLME-ASSOCIATE.confirm or + * MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATE.indication primitive). + * @NL80211_CMD_DISASSOCIATE: disassociation notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.indication primitive). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -217,6 +236,11 @@ enum nl80211_commands { NL80211_CMD_REG_CHANGE, + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -230,8 +254,11 @@ enum nl80211_commands { */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE - #define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE /** * enum nl80211_attrs - nl80211 netlink attributes @@ -353,6 +380,10 @@ enum nl80211_commands { * an array of command numbers (i.e. a mapping index to command number) * that the driver for the given wiphy supports. * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -432,6 +463,8 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORTED_COMMANDS, + NL80211_ATTR_FRAME, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -451,6 +484,7 @@ enum nl80211_attrs { #define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 50f3fd9ff52..ad44016021b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -807,4 +807,50 @@ void cfg80211_put_bss(struct cfg80211_bss *bss); */ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); +/** + * cfg80211_send_rx_auth - notification of processed authentication + * @dev: network device + * @buf: authentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever an authentication has been processed in + * station mode. + */ +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_rx_assoc - notification of processed association + * @dev: network device + * @buf: (re)association response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever a (re)association response has been + * processed in station mode. + */ +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len); + +/** + * cfg80211_send_rx_deauth - notification of processed deauthentication + * @dev: network device + * @buf: deauthentication frame (header + body) + * @len: length of the frame data + * + * This function is called whenever deauthentication has been processed in + * station mode. + */ +void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, + size_t len); + +/** + * cfg80211_send_rx_disassoc - notification of processed disassociation + * @dev: network device + * @buf: disassociation response frame (header + body) + * @len: length of the frame data + * + * This function is called whenever disassociation has been processed in + * station mode. + */ +void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, + size_t len); + #endif /* __NET_CFG80211_H */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1f49b63d8dd..6dc7a61bc18 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1085,11 +1085,13 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: ieee80211_auth_completed(sdata); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; case WLAN_AUTH_SHARED_KEY: - if (ifmgd->auth_transaction == 4) + if (ifmgd->auth_transaction == 4) { ieee80211_auth_completed(sdata); - else + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + } else ieee80211_auth_challenge(sdata, mgmt, len); break; } @@ -1125,6 +1127,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, true, false, 0); ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; + cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len); } @@ -1154,6 +1157,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, } ieee80211_set_disassoc(sdata, false, false, reason_code); + cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len); } @@ -1370,6 +1374,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_set_associated(sdata, changed); ieee80211_associated(sdata); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); } diff --git a/net/wireless/Makefile b/net/wireless/Makefile index c157b4d8014..6d1e7b27b75 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o 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 +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c new file mode 100644 index 00000000000..bec5721b6f9 --- /dev/null +++ b/net/wireless/mlme.c @@ -0,0 +1,46 @@ +/* + * cfg80211 MLME SAP interface + * + * Copyright (c) 2009, Jouni Malinen + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_auth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_auth); + +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_assoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_assoc); + +void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_deauth(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_deauth); + +void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_disassoc(rdev, dev, buf, len); +} +EXPORT_SYMBOL(cfg80211_send_rx_disassoc); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a3ecf8d7389..c034c2418cb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2830,6 +2830,9 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_scan, }, }; +static struct genl_multicast_group nl80211_mlme_mcgrp = { + .name = "mlme", +}; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { @@ -2975,6 +2978,71 @@ nla_put_failure: nlmsg_free(msg); } +static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, + enum nl80211_commands cmd) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_AUTHENTICATE); +} + +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE); +} + +void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DEAUTHENTICATE); +} + +void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_DISASSOCIATE); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -3003,6 +3071,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5b5fe1339de..b77af4ab80b 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -11,5 +11,17 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_reg_change_event(struct regulatory_request *request); +extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3-70-g09d2 From 636a5d3625993c5ca59abc81794b9ded93cdb740 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 19 Mar 2009 13:39:22 +0200 Subject: nl80211: Add MLME primitives to support external SME This patch adds new nl80211 commands to allow user space to request authentication and association (and also deauthentication and disassociation). The commands are structured to allow separate authentication and association steps, i.e., the interface between kernel and user space is similar to the MLME SAP interface in IEEE 802.11 standard and an user space application takes the role of the SME. The patch introduces MLME-AUTHENTICATE.request, MLME-{,RE}ASSOCIATE.request, MLME-DEAUTHENTICATE.request, and MLME-DISASSOCIATE.request primitives. The authentication and association commands request the actual operations in two steps (assuming the driver supports this; if not, separate authentication step is skipped; this could end up being a separate "connect" command). The initial implementation for mac80211 uses the current net/mac80211/mlme.c for actual sending and processing of management frames and the new nl80211 commands will just stop the current state machine from moving automatically from authentication to association. Future cleanup may move more of the MLME operations into cfg80211. The goal of this design is to provide more control of authentication and association process to user space without having to move the full MLME implementation. This should be enough to allow IEEE 802.11r FT protocol and 802.11s SAE authentication to be implemented. Obviously, this will also bring the extra benefit of not having to use WEXT for association requests with mac80211. An example implementation of a user space SME using the new nl80211 commands is available for wpa_supplicant. This patch is enough to get IEEE 802.11r FT protocol working with over-the-air mechanism (over-the-DS will need additional MLME primitives for handling the FT Action frames). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 1 + include/linux/nl80211.h | 58 +++++++++-- include/net/cfg80211.h | 113 ++++++++++++++++++++ net/mac80211/cfg.c | 140 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 7 +- net/mac80211/mlme.c | 45 ++++++-- net/mac80211/wext.c | 3 + net/wireless/nl80211.c | 255 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 601 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 382387e75b8..4b501b48ce8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -867,6 +867,7 @@ struct ieee80211_ht_info { /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 5ce68ae8314..9685eaab40a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -161,24 +161,37 @@ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on * to (%NL80211_ATTR_REG_ALPHA2). * - * @NL80211_CMD_AUTHENTICATE: authentication notification (on the "mlme" - * multicast group). This event reports reception of an Authentication + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication * frame in station and IBSS modes when the local MLME processed the * frame, i.e., it was for the local STA and was received in correct * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the * MLME SAP interface (kernel providing MLME, userspace SME). The * included NL80211_ATTR_FRAME attribute contains the management frame * (including both the header and frame body, but not FCS). - * @NL80211_CMD_ASSOCIATE: association notification; like - * NL80211_CMD_AUTHENTICATE but for Association Response and Reassociation - * Response frames (similar to MLME-ASSOCIATE.confirm or - * MLME-REASSOCIATE.confirm primitives). - * @NL80211_CMD_DEAUTHENTICATE: deauthentication notification; like + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to - * MLME-DEAUTHENTICATE.indication primitive). - * @NL80211_CMD_DISASSOCIATE: disassociation notification; like + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to - * MLME-DISASSOCIATE.indication primitive). + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -383,6 +396,11 @@ enum nl80211_commands { * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -464,6 +482,9 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORTED_COMMANDS, NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, /* add attributes here, update the policy in nl80211.c */ @@ -485,6 +506,9 @@ enum nl80211_attrs { #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE #define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -1018,4 +1042,18 @@ enum nl80211_bss { NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 }; +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, +}; #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ad44016021b..0da9a55881a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -578,6 +578,105 @@ struct cfg80211_bss { u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); }; +/** + * struct cfg80211_auth_request - Authentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * authentication. + * NOTE: This structure will likely change when more code from mac80211 is + * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. + * Before using this in a driver that does not use mac80211, it would be better + * to check the status of that work and better yet, volunteer to work on it. + * + * @chan: The channel to use or %NULL if not specified (auto-select based on + * scan results) + * @peer_addr: The address of the peer STA (AP BSSID in infrastructure case); + * this field is required to be present; if the driver wants to help with + * BSS selection, it should use (yet to be added) MLME event to allow user + * space SME to be notified of roaming candidate, so that the SME can then + * use the authentication request with the recommended BSSID and whatever + * other data may be needed for authentication/association + * @ssid: SSID or %NULL if not yet available + * @ssid_len: Length of ssid in octets + * @auth_type: Authentication type (algorithm) + * @ie: Extra IEs to add to Authentication frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_auth_request { + struct ieee80211_channel *chan; + u8 *peer_addr; + const u8 *ssid; + size_t ssid_len; + enum nl80211_auth_type auth_type; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_assoc_request - (Re)Association request data + * + * This structure provides information needed to complete IEEE 802.11 + * (re)association. + * NOTE: This structure will likely change when more code from mac80211 is + * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too. + * Before using this in a driver that does not use mac80211, it would be better + * to check the status of that work and better yet, volunteer to work on it. + * + * @chan: The channel to use or %NULL if not specified (auto-select based on + * scan results) + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be in State 2 (authenticated) with the + * peer STA + * @ssid: SSID + * @ssid_len: Length of ssid in octets + * @ie: Extra IEs to add to (Re)Association Request frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_assoc_request { + struct ieee80211_channel *chan; + u8 *peer_addr; + const u8 *ssid; + size_t ssid_len; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_deauth_request - Deauthentication request data + * + * This structure provides information needed to complete IEEE 802.11 + * deauthentication. + * + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be authenticated with the peer STA + * @ie: Extra IEs to add to Deauthentication frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_deauth_request { + u8 *peer_addr; + u16 reason_code; + const u8 *ie; + size_t ie_len; +}; + +/** + * struct cfg80211_disassoc_request - Disassociation request data + * + * This structure provides information needed to complete IEEE 802.11 + * disassocation. + * + * @peer_addr: The address of the peer STA (AP BSSID); this field is required + * to be present and the STA must be associated with the peer STA + * @ie: Extra IEs to add to Disassociation frame or %NULL + * @ie_len: Length of ie buffer in octets + */ +struct cfg80211_disassoc_request { + u8 *peer_addr; + u16 reason_code; + const u8 *ie; + size_t ie_len; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -650,6 +749,11 @@ struct cfg80211_bss { * the driver, and will be valid until passed to cfg80211_scan_done(). * For scan results, call cfg80211_inform_bss(); you can call this outside * the scan/scan_done bracket too. + * + * @auth: Request to authenticate with the specified peer + * @assoc: Request to (re)associate with the specified peer + * @deauth: Request to deauthenticate from the specified peer + * @disassoc: Request to disassociate from the specified peer */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -730,6 +834,15 @@ struct cfg80211_ops { int (*scan)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); + + int (*auth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req); + int (*assoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req); + int (*deauth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req); + int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req); }; /* temporary wext handlers */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 58693e52d45..223e536e842 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1300,6 +1300,142 @@ static int ieee80211_scan(struct wiphy *wiphy, return ieee80211_request_scan(sdata, req); } +static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_auth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP; + break; + default: + return -EOPNOTSUPP; + } + + memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } + + kfree(sdata->u.mgd.sme_auth_ie); + sdata->u.mgd.sme_auth_ie = NULL; + sdata->u.mgd.sme_auth_ie_len = 0; + if (req->ie) { + sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL); + if (sdata->u.mgd.sme_auth_ie == NULL) + return -ENOMEM; + memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len); + sdata->u.mgd.sme_auth_ie_len = req->ie_len; + } + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + int ret; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || + !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) + return -ENOLINK; /* not authenticated */ + + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + + /* TODO: req->chan */ + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + + if (req->ssid) { + sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; + } else + sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; + + ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); + if (ret) + return ret; + + sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; + sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; + ieee80211_sta_req_auth(sdata); + return 0; +} + +static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_deauthenticate(sdata, req->reason_code); +} + +static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + /* TODO: req->ie */ + return ieee80211_sta_disassociate(sdata, req->reason_code); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1333,4 +1469,8 @@ struct cfg80211_ops mac80211_config_ops = { .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, + .auth = ieee80211_auth, + .assoc = ieee80211_assoc, + .deauth = ieee80211_deauth, + .disassoc = ieee80211_disassoc, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ad12c2a03a9..7b96d95f48b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -256,6 +256,7 @@ struct mesh_preq_queue { #define IEEE80211_STA_TKIP_WEP_USED BIT(14) #define IEEE80211_STA_CSA_RECEIVED BIT(15) #define IEEE80211_STA_MFP_ENABLED BIT(16) +#define IEEE80211_STA_EXT_SME BIT(17) /* flags for MLME request */ #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_DIRECT_PROBE 1 @@ -266,6 +267,7 @@ struct mesh_preq_queue { #define IEEE80211_AUTH_ALG_OPEN BIT(0) #define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) #define IEEE80211_AUTH_ALG_LEAP BIT(2) +#define IEEE80211_AUTH_ALG_FT BIT(3) struct ieee80211_if_managed { struct timer_list timer; @@ -335,6 +337,9 @@ struct ieee80211_if_managed { size_t ie_deauth_len; u8 *ie_disassoc; size_t ie_disassoc_len; + + u8 *sme_auth_ie; + size_t sme_auth_ie_len; }; enum ieee80211_ibss_flags { @@ -970,7 +975,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - char *ie, size_t len); + const char *ie, size_t len); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); void ieee80211_scan_failed(struct ieee80211_local *local); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6dc7a61bc18..d1bcc843877 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -730,6 +730,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + u8 *ies; + size_t ies_len; ifmgd->auth_tries++; if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { @@ -755,7 +757,14 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticate with AP %pM\n", sdata->dev->name, ifmgd->bssid); - ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0, + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + ies = ifmgd->sme_auth_ie; + ies_len = ifmgd->sme_auth_ie_len; + } else { + ies = NULL; + ies_len = 0; + } + ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len, ifmgd->bssid, 0); ifmgd->auth_transaction = 2; @@ -870,7 +879,8 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) int wep_privacy; int privacy_invoked; - if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL)) + if (!ifmgd || (ifmgd->flags & (IEEE80211_STA_MIXED_CELL | + IEEE80211_STA_EXT_SME))) return 0; bss = ieee80211_rx_bss_get(local, ifmgd->bssid, @@ -998,7 +1008,11 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name); ifmgd->flags |= IEEE80211_STA_AUTHENTICATED; - ieee80211_associate(sdata); + if (ifmgd->flags & IEEE80211_STA_EXT_SME) { + /* Wait for SME to request association */ + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + } else + ieee80211_associate(sdata); } @@ -1084,6 +1098,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, switch (ifmgd->auth_alg) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: + case WLAN_AUTH_FT: ieee80211_auth_completed(sdata); cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); break; @@ -1117,9 +1132,10 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) { ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1150,7 +1166,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; mod_timer(&ifmgd->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1664,6 +1681,8 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY; else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) ifmgd->auth_alg = WLAN_AUTH_LEAP; + else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT) + ifmgd->auth_alg = WLAN_AUTH_FT; else ifmgd->auth_alg = WLAN_AUTH_OPEN; ifmgd->auth_transaction = -1; @@ -1687,7 +1706,8 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) u16 capa_val = WLAN_CAPABILITY_ESS; struct ieee80211_channel *chan = local->oper_channel; - if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && + ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL)) { capa_mask |= WLAN_CAPABILITY_PRIVACY; @@ -1884,7 +1904,11 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) ieee80211_set_disassoc(sdata, true, true, WLAN_REASON_DEAUTH_LEAVING); - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) || + ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) + set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); + else if (ifmgd->flags & IEEE80211_STA_EXT_SME) + set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); queue_work(local->hw.workqueue, &ifmgd->work); } } @@ -1953,7 +1977,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) return ieee80211_sta_commit(sdata); } -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len) +int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, + const char *ie, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index e55d2834764..ce21d66b102 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -137,6 +137,7 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, if (ret) return ret; sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } @@ -224,6 +225,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) @@ -287,6 +289,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); if (ret) return ret; + sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; ieee80211_sta_req_auth(sdata); return 0; } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c034c2418cb..9e1318d1d4b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -111,6 +111,11 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, + + [NL80211_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, }; /* message building helper */ @@ -265,6 +270,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); #undef CMD nla_nest_end(msg, nl_cmds); @@ -2646,6 +2655,228 @@ static int nl80211_dump_scan(struct sk_buff *skb, return err; } +static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_auth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->auth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (info->attrs[NL80211_ATTR_SSID]) { + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + } + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + req.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + } + + err = drv->ops->auth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_assoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->assoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_SSID]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + req.chan = ieee80211_get_channel( + wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!req.chan) { + err = -EINVAL; + goto out; + } + } + + if (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out; + } + req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->assoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_deauth_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->deauth) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->deauth(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_disassoc_request req; + struct wiphy *wiphy; + int err; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (!drv->ops->disassoc) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } + + wiphy = &drv->wiphy; + memset(&req, 0, sizeof(req)); + + req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_REASON_CODE]) + req.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (info->attrs[NL80211_ATTR_IE]) { + req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = drv->ops->disassoc(&drv->wiphy, dev, &req); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2829,6 +3060,30 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .dumpit = nl80211_dump_scan, }, + { + .cmd = NL80211_CMD_AUTHENTICATE, + .doit = nl80211_authenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_ASSOCIATE, + .doit = nl80211_associate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEAUTHENTICATE, + .doit = nl80211_deauthenticate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISASSOCIATE, + .doit = nl80211_disassociate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", -- cgit v1.2.3-70-g09d2 From 65fc73ac4a310945dfeceac961726c2765ad2ec0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 20 Mar 2009 21:21:16 +0200 Subject: nl80211: Remove NL80211_CMD_SET_MGMT_EXTRA_IE The functionality that NL80211_CMD_SET_MGMT_EXTRA_IE provided can now be achieved with cleaner design by adding IE(s) into NL80211_CMD_TRIGGER_SCAN, NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. Since this is a very recently added command and there are no known (or known planned) applications using NL80211_CMD_SET_MGMT_EXTRA_IE and taken into account how much extra complexity it adds to the IE processing we have now (and need to add in the future to fix IE order in couple of frames), it looks like the best option is to just remove the implementation of this command for now. The enum values themselves are left to avoid changing the nl80211 command or attribute numbers. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++- include/net/cfg80211.h | 26 -------------- net/mac80211/cfg.c | 86 ---------------------------------------------- net/mac80211/ieee80211_i.h | 15 -------- net/mac80211/iface.c | 7 ---- net/mac80211/mlme.c | 36 ++----------------- net/mac80211/util.c | 29 +++------------- net/wireless/nl80211.c | 47 ------------------------- 8 files changed, 14 insertions(+), 240 deletions(-) (limited to 'include/net') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 9685eaab40a..cbe8ce3bf48 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -142,6 +142,12 @@ * %NL80211_ATTR_IE. If the command succeeds, the requested data will be * added to all specified management frames generated by * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters @@ -238,7 +244,7 @@ enum nl80211_commands { NL80211_CMD_GET_MESH_PARAMS, NL80211_CMD_SET_MESH_PARAMS, - NL80211_CMD_SET_MGMT_EXTRA_IE, + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, NL80211_CMD_GET_REG, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0da9a55881a..dca4a6b0461 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -471,26 +471,6 @@ struct ieee80211_txq_params { u8 aifs; }; -/** - * struct mgmt_extra_ie_params - Extra management frame IE parameters - * - * Used to add extra IE(s) into management frames. If the driver cannot add the - * requested data into all management frames of the specified subtype that are - * generated in kernel or firmware/hardware, it must reject the configuration - * call. The IE data buffer is added to the end of the specified management - * frame body after all other IEs. This addition is not applied to frames that - * are injected through a monitor interface. - * - * @subtype: Management frame subtype - * @ies: IE data buffer or %NULL to remove previous data - * @ies_len: Length of @ies in octets - */ -struct mgmt_extra_ie_params { - u8 subtype; - u8 *ies; - int ies_len; -}; - /* from net/wireless.h */ struct wiphy; @@ -743,8 +723,6 @@ struct cfg80211_disassoc_request { * * @set_channel: Set channel * - * @set_mgmt_extra_ie: Set extra IE data for management frames - * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). * For scan results, call cfg80211_inform_bss(); you can call this outside @@ -828,10 +806,6 @@ struct cfg80211_ops { struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); - int (*set_mgmt_extra_ie)(struct wiphy *wiphy, - struct net_device *dev, - struct mgmt_extra_ie_params *params); - int (*scan)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 223e536e842..f5c15c9a00c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1181,91 +1181,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } -static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata, - u8 subtype, u8 *ies, size_t ies_len) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - switch (subtype) { - case IEEE80211_STYPE_PROBE_REQ >> 4: - if (local->ops->hw_scan) - break; - kfree(ifmgd->ie_probereq); - ifmgd->ie_probereq = ies; - ifmgd->ie_probereq_len = ies_len; - return 0; - case IEEE80211_STYPE_PROBE_RESP >> 4: - kfree(ifmgd->ie_proberesp); - ifmgd->ie_proberesp = ies; - ifmgd->ie_proberesp_len = ies_len; - return 0; - case IEEE80211_STYPE_AUTH >> 4: - kfree(ifmgd->ie_auth); - ifmgd->ie_auth = ies; - ifmgd->ie_auth_len = ies_len; - return 0; - case IEEE80211_STYPE_ASSOC_REQ >> 4: - kfree(ifmgd->ie_assocreq); - ifmgd->ie_assocreq = ies; - ifmgd->ie_assocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_REASSOC_REQ >> 4: - kfree(ifmgd->ie_reassocreq); - ifmgd->ie_reassocreq = ies; - ifmgd->ie_reassocreq_len = ies_len; - return 0; - case IEEE80211_STYPE_DEAUTH >> 4: - kfree(ifmgd->ie_deauth); - ifmgd->ie_deauth = ies; - ifmgd->ie_deauth_len = ies_len; - return 0; - case IEEE80211_STYPE_DISASSOC >> 4: - kfree(ifmgd->ie_disassoc); - ifmgd->ie_disassoc = ies; - ifmgd->ie_disassoc_len = ies_len; - return 0; - } - - return -EOPNOTSUPP; -} - -static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, - struct net_device *dev, - struct mgmt_extra_ie_params *params) -{ - struct ieee80211_sub_if_data *sdata; - u8 *ies; - size_t ies_len; - int ret = -EOPNOTSUPP; - - if (params->ies) { - ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); - if (ies == NULL) - return -ENOMEM; - ies_len = params->ies_len; - } else { - ies = NULL; - ies_len = 0; - } - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - ret = set_mgmt_extra_ie_sta(sdata, params->subtype, - ies, ies_len); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - if (ret) - kfree(ies); - return ret; -} - #ifdef CONFIG_PM static int ieee80211_suspend(struct wiphy *wiphy) { @@ -1465,7 +1380,6 @@ struct cfg80211_ops mac80211_config_ops = { .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_channel = ieee80211_set_channel, - .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 547cfac218e..f69e84ab961 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -323,21 +323,6 @@ struct ieee80211_if_managed { int wmm_last_param_set; /* Extra IE data for management frames */ - u8 *ie_probereq; - size_t ie_probereq_len; - u8 *ie_proberesp; - size_t ie_proberesp_len; - u8 *ie_auth; - size_t ie_auth_len; - u8 *ie_assocreq; - size_t ie_assocreq_len; - u8 *ie_reassocreq; - size_t ie_reassocreq_len; - u8 *ie_deauth; - size_t ie_deauth_len; - u8 *ie_disassoc; - size_t ie_disassoc_len; - u8 *sme_auth_ie; size_t sme_auth_ie_len; }; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6b56dc2208e..34f4798a98f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -653,13 +653,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.mgd.extra_ie); kfree(sdata->u.mgd.assocreq_ies); kfree(sdata->u.mgd.assocresp_ies); - kfree(sdata->u.mgd.ie_probereq); - kfree(sdata->u.mgd.ie_proberesp); - kfree(sdata->u.mgd.ie_auth); - kfree(sdata->u.mgd.ie_assocreq); - kfree(sdata->u.mgd.ie_reassocreq); - kfree(sdata->u.mgd.ie_deauth); - kfree(sdata->u.mgd.ie_disassoc); kfree(sdata->u.mgd.sme_auth_ie); break; case NL80211_IFTYPE_WDS: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d1bcc843877..b0808efcedf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -82,38 +82,23 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, /* frame sending functions */ -static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) -{ - if (ies) - memcpy(skb_put(skb, ies_len), ies, ies_len); -} - static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie, *e_ies; + u8 *pos, *ies, *ht_ie; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u32 rates = 0; - size_t e_ies_len; - - if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { - e_ies = sdata->u.mgd.ie_reassocreq; - e_ies_len = sdata->u.mgd.ie_reassocreq_len; - } else { - e_ies = sdata->u.mgd.ie_assocreq; - e_ies_len = sdata->u.mgd.ie_assocreq_len; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifmgd->extra_ie_len + - ifmgd->ssid_len + e_ies_len); + ifmgd->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -304,8 +289,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } - add_extra_ies(skb, e_ies, e_ies_len); - kfree(ifmgd->assocreq_ies); ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies; ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL); @@ -323,19 +306,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *ies; - size_t ies_len; - if (stype == IEEE80211_STYPE_DEAUTH) { - ies = sdata->u.mgd.ie_deauth; - ies_len = sdata->u.mgd.ie_deauth_len; - } else { - ies = sdata->u.mgd.ie_disassoc; - ies_len = sdata->u.mgd.ie_disassoc_len; - } - - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + - ies_len); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for " "deauth/disassoc frame\n", sdata->dev->name); @@ -353,8 +325,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - add_extra_ies(skb, ies, ies_len); - ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e0431a1d218..444bb14c95e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -846,16 +846,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - const u8 *ie_auth = NULL; - int ie_auth_len = 0; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - ie_auth_len = sdata->u.mgd.ie_auth_len; - ie_auth = sdata->u.mgd.ie_auth; - } skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len + ie_auth_len); + sizeof(*mgmt) + 6 + extra_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", sdata->dev->name); @@ -877,8 +870,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); - if (ie_auth) - memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len); ieee80211_tx_skb(sdata, skb, encrypt); } @@ -891,20 +882,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, struct ieee80211_supported_band *sband; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL; - int i, extra_preq_ie_len = 0; - - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - extra_preq_ie_len = sdata->u.mgd.ie_probereq_len; - extra_preq_ie = sdata->u.mgd.ie_probereq; - break; - default: - break; - } + u8 *pos, *supp_rates, *esupp_rates = NULL; + int i; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + - ie_len + extra_preq_ie_len); + ie_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -953,9 +935,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, if (ie) memcpy(skb_put(skb, ie_len), ie, ie_len); - if (extra_preq_ie) - memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie, - extra_preq_ie_len); ieee80211_tx_skb(sdata, skb, 0); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9e1318d1d4b..44c79972be5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -269,7 +269,6 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_mpath, NEW_MPATH); CMD(set_mesh_params, SET_MESH_PARAMS); CMD(change_bss, SET_BSS); - CMD(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); @@ -2355,46 +2354,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } -static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *drv; - int err; - struct net_device *dev; - struct mgmt_extra_ie_params params; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) - return -EINVAL; - params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); - if (params.subtype > 15) - return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ - - if (info->attrs[NL80211_ATTR_IE]) { - params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - rtnl_lock(); - - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); - if (err) - goto out_rtnl; - - if (drv->ops->set_mgmt_extra_ie) - err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); - else - err = -EOPNOTSUPP; - - cfg80211_put_dev(drv); - dev_put(dev); - out_rtnl: - rtnl_unlock(); - - return err; -} - static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3043,12 +3002,6 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, - { - .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, - .doit = nl80211_set_mgmt_extra_ie, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, { .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, -- cgit v1.2.3-70-g09d2 From 9050bdd8589c373e01e41ddbd9a192de2ff01ef0 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:21 +0200 Subject: mac80211: disable power save when scanning When software scanning we need to disable power save so that all possible probe responses and beacons are received. For hardware scanning assume that hardware will take care of that and document that assumption. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/mac80211.h | 12 ++++++---- net/mac80211/scan.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index daa539a4287..174dc1d7526 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1307,11 +1307,13 @@ enum ieee80211_ampdu_mlme_action { * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel - * configuration done by the regulatory agent in the wiphy's registered - * bands. When the scan finishes, ieee80211_scan_completed() must be - * called; note that it also must be called when the scan cannot finish - * because the hardware is turned off! Anything else is a bug! - * Returns a negative error code which will be seen in userspace. + * configuration done by the regulatory agent in the wiphy's + * registered bands. The hardware (or the driver) needs to make sure + * that power save is disabled. When the scan finishes, + * ieee80211_scan_completed() must be called; note that it also must + * be called when the scan cannot finish because the hardware is + * turned off! Anything else is a bug! Returns a negative error code + * which will be seen in userspace. * * @sw_scan_start: Notifier function that is called just before a software scan * is started. Can be NULL, if the driver doesn't need this notification. diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 46f35dc6acc..3bf9839f591 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -214,6 +214,66 @@ void ieee80211_scan_failed(struct ieee80211_local *local) local->scan_req = NULL; } +/* + * inform AP that we will go to sleep so that it will buffer the frames + * while we scan + */ +static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + bool ps = false; + + /* FIXME: what to do when local->pspolling is true? */ + + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + ps = true; + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } + + if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) + /* + * If power save was enabled, no need to send a nullfunc + * frame because AP knows that we are sleeping. But if the + * hardware is creating the nullfunc frame for power save + * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not + * enabled) and power save was enabled, the firmware just + * sent a null frame with power save disabled. So we need + * to send a new nullfunc frame to inform the AP that we + * are again sleeping. + */ + ieee80211_send_nullfunc(local, sdata, 1); +} + +/* inform AP that we are awake again, unless power save is enabled */ +static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + + if (!local->powersave) + ieee80211_send_nullfunc(local, sdata, 0); + else { + /* + * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware + * will send a nullfunc frame with the powersave bit set + * even though the AP already knows that we are sleeping. + * This could be avoided by sending a null frame with power + * save bit disabled before enabling the power save, but + * this doesn't gain anything. + * + * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need + * to send a nullfunc frame because AP already knows that + * we are sleeping, let's just enable power save mode in + * hardware. + */ + local->hw.conf.flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } +} + void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); @@ -268,7 +328,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) /* Tell AP we're back */ if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { - ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_scan_ps_disable(sdata); netif_tx_wake_all_queues(sdata->dev); } } else @@ -441,7 +501,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { netif_tx_stop_all_queues(sdata->dev); - ieee80211_send_nullfunc(local, sdata, 1); + ieee80211_scan_ps_enable(sdata); } } else netif_tx_stop_all_queues(sdata->dev); -- cgit v1.2.3-70-g09d2 From a08c1c1ac0c26229ca1ca45d554b209a56edc8be Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:28 +0200 Subject: cfg80211: add feature to hold bss In beacon filtering there needs to be a way to not expire the BSS even when no beacons are received. Add an interface to cfg80211 to hold BSS and make sure that it's not expired. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- include/net/cfg80211.h | 18 ++++++++++++++++++ net/wireless/core.h | 2 ++ net/wireless/scan.c | 27 ++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dca4a6b0461..5389afdc129 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -539,6 +539,7 @@ enum cfg80211_signal_type { * is no guarantee that these are well-formed!) * @len_information_elements: total length of the information elements * @signal: signal strength value (type depends on the wiphy's signal_type) + * @hold: BSS should not expire * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ @@ -940,4 +941,21 @@ void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf, size_t len); +/** + * cfg80211_hold_bss - exclude bss from expiration + * @bss: bss which should not expire + * + * In a case when the BSS is not updated but it shouldn't expire this + * function can be used to mark the BSS to be excluded from expiration. + */ +void cfg80211_hold_bss(struct cfg80211_bss *bss); + +/** + * cfg80211_unhold_bss - remove expiration exception from the BSS + * @bss: bss which can expire again + * + * This function marks the BSS to be expirable again. + */ +void cfg80211_unhold_bss(struct cfg80211_bss *bss); + #endif /* __NET_CFG80211_H */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 6acd483a61f..97a6fd8b2b0 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -90,6 +90,8 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; + bool hold; + /* must be last because of priv member */ struct cfg80211_bss pub; }; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 280dbcd02c1..2a00e362f5f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -80,7 +80,8 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (bss->hold || + !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -471,6 +472,30 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_unlink_bss); +void cfg80211_hold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = true; +} +EXPORT_SYMBOL(cfg80211_hold_bss); + +void cfg80211_unhold_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss->hold = false; +} +EXPORT_SYMBOL(cfg80211_unhold_bss); + #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3-70-g09d2 From 04de83815993714a7ba2618f637fa1092a5f664b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:35 +0200 Subject: mac80211: add beacon filtering support Add IEEE80211_HW_BEACON_FILTERING flag so that driver inform that it supports beacon filtering. Drivers need to call the new function ieee80211_beacon_loss() to notify about beacon loss. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 6 +++++ include/net/mac80211.h | 33 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 3 +++ net/mac80211/mlme.c | 49 ++++++++++++++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index 8af6d962687..fbeaffc1dcc 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -227,6 +227,12 @@ usage should require reading the full document. !Pinclude/net/mac80211.h Powersave support + + Beacon filter support +!Pinclude/net/mac80211.h Beacon filter support +!Finclude/net/mac80211.h ieee80211_beacon_loss + + Multiple queues and QoS support TBD diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 174dc1d7526..d881ed8ad2c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -882,6 +882,10 @@ enum ieee80211_tkip_key_type { * * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). + * + * @IEEE80211_HW_BEACON_FILTER: + * Hardware supports dropping of irrelevant beacon frames to + * avoid waking up cpu. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -897,6 +901,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, + IEEE80211_HW_BEACON_FILTER = 1<<14, }; /** @@ -1120,6 +1125,24 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * value, or by the stack if all nullfunc handling is in the stack. */ +/** + * DOC: Beacon filter support + * + * Some hardware have beacon filter support to reduce host cpu wakeups + * which will reduce system power consumption. It usuallly works so that + * the firmware creates a checksum of the beacon but omits all constantly + * changing elements (TSF, TIM etc). Whenever the checksum changes the + * beacon is forwarded to the host, otherwise it will be just dropped. That + * way the host will only receive beacons where some relevant information + * (for example ERP protection or WMM settings) have changed. + * + * Beacon filter support is informed with %IEEE80211_HW_BEACON_FILTER flag. + * The driver needs to enable beacon filter support whenever power save is + * enabled, that is %IEEE80211_CONF_PS is set. When power save is enabled, + * the stack will not check for beacon miss at all and the driver needs to + * notify about complete loss of beacons with ieee80211_beacon_loss(). + */ + /** * DOC: Frame filtering * @@ -1970,6 +1993,16 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, const u8 *addr); +/** + * ieee80211_beacon_loss - inform hardware does not receive beacons + * + * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf. + * + * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and + * IEEE80211_CONF_PS is set, the driver needs to inform whenever the + * hardware is not receiving beacons with this function. + */ +void ieee80211_beacon_loss(struct ieee80211_vif *vif); /* Rate control API */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a617a7fc09..acba78e1a5c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -275,6 +275,7 @@ struct ieee80211_if_managed { struct timer_list chswitch_timer; struct work_struct work; struct work_struct chswitch_work; + struct work_struct beacon_loss_work; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -1086,6 +1087,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); +void ieee80211_beacon_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index dd2a276fa8c..91e8e1bacaa 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -477,6 +477,9 @@ static int ieee80211_stop(struct net_device *dev) */ cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); + + cancel_work_sync(&sdata->u.mgd.beacon_loss_work); + /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f30f4d19da..7ecda9d59d8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -610,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); + cfg80211_hold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); } @@ -751,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local_to_hw(local)->conf; + struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = 0, config_changed = 0; @@ -774,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_sta_tear_down_BA_sessions(sta); + bss = ieee80211_rx_bss_get(local, ifmgd->bssid, + conf->channel->center_freq, + ifmgd->ssid, ifmgd->ssid_len); + + if (bss) { + cfg80211_unhold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); + } + if (self_disconnected) { if (deauth) ieee80211_send_deauth_disassoc(sdata, @@ -925,6 +938,33 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, jiffies + IEEE80211_MONITORING_INTERVAL); } +void ieee80211_beacon_loss_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.beacon_loss_work); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " + "- sending probe request\n", sdata->dev->name, + sdata->u.mgd.bssid); + + 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_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.beacon_loss_work); +} +EXPORT_SYMBOL(ieee80211_beacon_loss); + static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -959,7 +999,13 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) goto unlock; } - if (time_after(jiffies, + /* + * Beacon filtering is only enabled with power save and then the + * stack should not check for beacon loss. + */ + if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) && + (local->hw.conf.flags & IEEE80211_CONF_PS)) && + time_after(jiffies, ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { printk(KERN_DEBUG "%s: beacon loss from AP %pM " "- sending probe request\n", @@ -1869,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); + INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, -- cgit v1.2.3-70-g09d2 From 2b874e83c970b45c328ab12239b066a43505454c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 14:10:22 +0100 Subject: mac80211: rate control status only for controlled packets This patch changes mac80211 to not notify the rate control algorithm's tx_status() method when reporting status for a packet that didn't go through the rate control algorithm's get_rate() method. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/rate.c | 6 ++++-- net/mac80211/rate.h | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d881ed8ad2c..6f3bc4cc53e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -245,6 +245,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be * set by rate control algorithms to indicate probe rate, will * be cleared for fragmented frames (except on the last fragment) + * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or + * set this flag in the driver; indicates that the rate control + * algorithm was used and should be notified of TX status */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -260,6 +263,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU = BIT(10), IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), + IEEE80211_TX_INTFL_RCALGO = BIT(13), }; /** diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3fa7ab28506..4641f00a1e5 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -219,10 +219,12 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, info->control.rates[i].count = 1; } - if (sta && sdata->force_unicast_rateidx > -1) + if (sta && sdata->force_unicast_rateidx > -1) { info->control.rates[0].idx = sdata->force_unicast_rateidx; - else + } else { ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); + info->flags |= IEEE80211_TX_INTFL_RCALGO; + } /* * try to enforce the maximum rate the user wanted diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index b9164c9a956..2ab5ad9e71c 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -44,8 +44,10 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO)) + ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); } -- cgit v1.2.3-70-g09d2 From b1720231ca07dee3382980f3b25e6581bd2e54e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:39 +0100 Subject: mac80211: unify and fix TX aggregation start When TX aggregation becomes operational, we do a number of steps: 1) print a debug message 2) wake the virtual queue 3) notify the driver Unfortunately, 1) and 3) are only done if the driver is first to reply to the aggregation request, it is, however, possible that the remote station replies before the driver! Thus, unify the code for this and call the new function ieee80211_agg_tx_operational in both places where TX aggregation can become operational. Additionally, rename the driver notification from IEEE80211_AMPDU_TX_RESUME to IEEE80211_AMPDU_TX_OPERATIONAL. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- include/net/mac80211.h | 4 +-- net/mac80211/agg-tx.c | 63 +++++++++++++++++---------------------- 3 files changed, 30 insertions(+), 39 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index c13e4e53634..13d4e6756c9 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -2730,7 +2730,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_RESUME: + case IEEE80211_AMPDU_TX_OPERATIONAL: ath_tx_aggr_resume(sc, sta, tid); break; default: diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6f3bc4cc53e..07fe9875506 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1236,14 +1236,14 @@ enum ieee80211_filter_flags { * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation * @IEEE80211_AMPDU_TX_START: start Tx aggregation * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation - * @IEEE80211_AMPDU_TX_RESUME: resume TX aggregation + * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational */ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_TX_START, IEEE80211_AMPDU_TX_STOP, - IEEE80211_AMPDU_TX_RESUME, + IEEE80211_AMPDU_TX_OPERATIONAL, }; /** diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e5776ef1717..fd718e2b29f 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -404,6 +404,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +static void ieee80211_agg_tx_operational(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); +#endif + + if (local->hw.ampdu_queues) { + /* + * Wake up the A-MPDU queue, we stopped it earlier, + * this will in turn wake the entire AC. + */ + ieee80211_wake_queue_by_reason(&local->hw, + local->hw.queues + sta->tid_to_tx_q[tid], + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + } + + local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, + &sta->sta, tid, NULL); +} + void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) { struct ieee80211_local *local = hw_to_local(hw); @@ -446,20 +467,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state |= HT_ADDBA_DRV_READY_MSK; - if (*state == HT_AGG_STATE_OPERATIONAL) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); -#endif - if (hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - } + if (*state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); out: spin_unlock_bh(&sta->lock); @@ -646,9 +655,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len) { - struct ieee80211_hw *hw = &local->hw; - u16 capab; - u16 tid, start_seq_num; + u16 capab, tid; u8 *state; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); @@ -682,26 +689,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, *state |= HT_ADDBA_RECEIVED_MSK; - if (hw->ampdu_queues && *state != curstate && - *state == HT_AGG_STATE_OPERATIONAL) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - sta->ampdu_mlme.addba_req_num[tid] = 0; + if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL) + ieee80211_agg_tx_operational(local, sta, tid); - if (local->ops->ampdu_action) { - (void)local->ops->ampdu_action(hw, - IEEE80211_AMPDU_TX_RESUME, - &sta->sta, tid, &start_seq_num); - } -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + sta->ampdu_mlme.addba_req_num[tid] = 0; } else { sta->ampdu_mlme.addba_req_num[tid]++; ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); -- cgit v1.2.3-70-g09d2 From cd8ffc800ce18e558335c4946b2217864fc16045 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:41 +0100 Subject: mac80211: fix aggregation to not require queue stop Instead of stopping the entire AC queue when enabling aggregation (which was only done for hardware with aggregation queues) buffer the packets for each station, and release them to the pending skb queue once aggregation is turned on successfully. We get a little more code, but it becomes conceptually simpler and we can remove the entire virtual queue mechanism from mac80211 in a follow-up patch. This changes how mac80211 behaves towards drivers that support aggregation but have no hardware queues -- those drivers will now not be handed packets while the aggregation session is being established, but only after it has been fully established. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++ net/mac80211/agg-tx.c | 136 ++++++++++++++++++++++++++----------------- net/mac80211/ieee80211_i.h | 8 +++ net/mac80211/main.c | 2 + net/mac80211/sta_info.c | 5 ++ net/mac80211/sta_info.h | 2 + net/mac80211/tx.c | 142 ++++++++++++++++++++++++++++++++++++--------- 7 files changed, 221 insertions(+), 78 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 07fe9875506..841f7f804bb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -248,6 +248,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or * set this flag in the driver; indicates that the rate control * algorithm was used and should be notified of TX status + * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, + * used to indicate that a pending frame requires TX processing before + * it can be sent out. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -264,6 +267,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), IEEE80211_TX_INTFL_RCALGO = BIT(13), + IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), }; /** diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index fd718e2b29f..64b839bfbf1 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -132,16 +132,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; if (local->hw.ampdu_queues) { - if (initiator) { - /* - * Stop the AC queue to avoid issues where we send - * unaggregated frames already before the delba. - */ - ieee80211_stop_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } - /* * Pretend the driver woke the queue, just in case * it disabled it before the session was stopped. @@ -158,6 +148,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { *state = HT_AGG_STATE_OPERATIONAL; + /* + * We may have pending packets get stuck in this case... + * Not bothering with a workaround for now. + */ } return ret; @@ -226,13 +220,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "rejecting on voice AC\n"); -#endif - return -EINVAL; - } - rcu_read_lock(); sta = sta_info_get(local, ra); @@ -267,6 +254,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); sdata = sta->sdata; @@ -308,21 +296,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) ret = -ENOSPC; goto err_unlock_sta; } - - /* - * If we successfully allocate the session, we can't have - * anything going on on the queue this TID maps into, so - * stop it for now. This is a "virtual" stop using the same - * mechanism that drivers will use. - * - * XXX: queue up frames for this session in the sta_info - * struct instead to avoid hitting all other STAs. - */ - ieee80211_stop_queue_by_reason( - &local->hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); } + /* + * While we're asking the driver about the aggregation, + * stop the AC queue so that we don't have to worry + * about frames that came in while we were doing that, + * which would require us to put them to the AC pending + * afterwards which just makes the code more complex. + */ + ieee80211_stop_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + /* prepare A-MPDU MLME for Tx aggregation */ sta->ampdu_mlme.tid_tx[tid] = kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); @@ -336,6 +322,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_return_queue; } + skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); + /* Tx timer */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = sta_addba_resp_timer_expired; @@ -362,6 +350,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } sta->tid_to_tx_q[tid] = qn; + /* Driver vetoed or OKed, but we can take packets again now */ + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -388,15 +382,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.tid_tx[tid] = NULL; err_return_queue: if (qn >= 0) { - /* We failed, so start queue again right away. */ - ieee80211_wake_queue_by_reason(hw, hw->queues + qn, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); /* give queue back to pool */ spin_lock(&local->queue_stop_reason_lock); local->ampdu_ac_queue[qn] = -1; spin_unlock(&local->queue_stop_reason_lock); } + ieee80211_wake_queue_by_reason( + &local->hw, ieee80211_ac_from_tid(tid), + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); err_unlock_sta: + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); unlock: rcu_read_unlock(); @@ -404,6 +399,45 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish and holding + * local->ampdu_lock across both calls. + */ +static void ieee80211_agg_splice_packets(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + unsigned long flags; + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_stop_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + + if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + /* mark queue as pending, it is stopped already */ + __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, + &local->queue_stop_reasons[queue]); + /* copy over remaining packets */ + skb_queue_splice_tail_init( + &sta->ampdu_mlme.tid_tx[tid]->pending, + &local->pending[queue]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } +} + +static void ieee80211_agg_splice_finish(struct ieee80211_local *local, + struct sta_info *sta, u16 tid) +{ + u16 queue = ieee80211_ac_from_tid(tid); + + ieee80211_wake_queue_by_reason( + &local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); +} + +/* caller must hold sta->lock */ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { @@ -411,15 +445,16 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); #endif - if (local->hw.ampdu_queues) { - /* - * Wake up the A-MPDU queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + spin_lock(&local->ampdu_lock); + ieee80211_agg_splice_packets(local, sta, tid); + /* + * NB: we rely on sta->lock being taken in the TX + * processing here when adding to the pending queue, + * otherwise we could only change the state of the + * session to OPERATIONAL _here_. + */ + ieee80211_agg_splice_finish(local, sta, tid); + spin_unlock(&local->ampdu_lock); local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, &sta->sta, tid, NULL); @@ -602,22 +637,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); spin_lock_bh(&sta->lock); + spin_lock(&local->ampdu_lock); - if (*state & HT_AGG_STATE_INITIATOR_MSK && - hw->ampdu_queues) { - /* - * Wake up this queue, we stopped it earlier, - * this will in turn wake the entire AC. - */ - ieee80211_wake_queue_by_reason(hw, - hw->queues + sta->tid_to_tx_q[tid], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - } + ieee80211_agg_splice_packets(local, sta, tid); *state = HT_AGG_STATE_IDLE; + /* from now on packets are no longer put onto sta->pending */ sta->ampdu_mlme.addba_req_num[tid] = 0; kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; + + ieee80211_agg_splice_finish(local, sta, tid); + + spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); rcu_read_unlock(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6ce62e553dc..32345b479ad 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -639,6 +639,14 @@ struct ieee80211_local { struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; + /* + * This lock is used to prevent concurrent A-MPDU + * session start/stop processing, this thus also + * synchronises the ->ampdu_action() callback to + * drivers and limits it to one at a time. + */ + spinlock_t ampdu_lock; + /* number of interfaces with corresponding IFF_ flags */ atomic_t iff_allmultis, iff_promiscs; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a7430e98c53..756284e0bbd 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -795,6 +795,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); + spin_lock_init(&local->ampdu_lock); + return local_to_hw(local); } EXPORT_SYMBOL(ieee80211_alloc_hw); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4ba3c540fcf..dd3593c1fd2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -239,6 +239,11 @@ void sta_info_destroy(struct sta_info *sta) tid_tx = sta->ampdu_mlme.tid_tx[i]; if (tid_tx) { del_timer_sync(&tid_tx->addba_resp_timer); + /* + * STA removed while aggregation session being + * started? Bit odd, but purge frames anyway. + */ + skb_queue_purge(&tid_tx->pending); kfree(tid_tx); } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5b223b216e5..18fd5d1a442 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -73,11 +73,13 @@ enum ieee80211_sta_info_flags { * struct tid_ampdu_tx - TID aggregation information (Tx). * * @addba_resp_timer: timer for peer's response to addba request + * @pending: pending frames queue -- use sta's spinlock to protect * @ssn: Starting Sequence Number expected to be aggregated. * @dialog_token: dialog token for aggregation session */ struct tid_ampdu_tx { struct timer_list addba_resp_timer; + struct sk_buff_head pending; u16 ssn; u8 dialog_token; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a0e00c6339c..906ab785db4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -984,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int hdrlen, tid; u8 *qc, *state; + bool queued = false; memset(tx, 0, sizeof(*tx)); tx->skb = skb; @@ -1013,20 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, */ } + /* + * If this flag is set to true anywhere, and we get here, + * we are doing the needed processing, so remove the flag + * now. + */ + info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING; + hdr = (struct ieee80211_hdr *) skb->data; tx->sta = sta_info_get(local, hdr->addr1); - if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) { + if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && + (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { unsigned long flags; + struct tid_ampdu_tx *tid_tx; + qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; spin_lock_irqsave(&tx->sta->lock, flags); + /* + * XXX: This spinlock could be fairly expensive, but see the + * comment in agg-tx.c:ieee80211_agg_tx_operational(). + * One way to solve this would be to do something RCU-like + * for managing the tid_tx struct and using atomic bitops + * for the actual state -- by introducing an actual + * 'operational' bit that would be possible. It would + * require changing ieee80211_agg_tx_operational() to + * set that bit, and changing the way tid_tx is managed + * everywhere, including races between that bit and + * tid_tx going away (tid_tx being added can be easily + * committed to memory before the 'operational' bit). + */ + tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; - if (*state == HT_AGG_STATE_OPERATIONAL) + if (*state == HT_AGG_STATE_OPERATIONAL) { info->flags |= IEEE80211_TX_CTL_AMPDU; + } else if (*state != HT_AGG_STATE_IDLE) { + /* in progress */ + queued = true; + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + __skb_queue_tail(&tid_tx->pending, skb); + } spin_unlock_irqrestore(&tx->sta->lock, flags); + + if (unlikely(queued)) + return TX_QUEUED; } if (is_multicast_ether_addr(hdr->addr1)) { @@ -1077,7 +1110,14 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local, } if (unlikely(!dev)) return -ENODEV; - /* initialises tx with control */ + /* + * initialises tx with control + * + * return value is safe to ignore here because this function + * can only be invoked for multicast frames + * + * XXX: clean up + */ __ieee80211_tx_prepare(tx, skb, dev); dev_put(dev); return 0; @@ -1188,7 +1228,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) +static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, + bool txpending) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; @@ -1202,11 +1243,11 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) queue = skb_get_queue_mapping(skb); - WARN_ON(!skb_queue_empty(&local->pending[queue])); + WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue])); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); - return 0; + return; } rcu_read_lock(); @@ -1214,10 +1255,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) /* initialises tx */ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev); - if (res_prepare == TX_DROP) { + if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); rcu_read_unlock(); - return 0; + return; + } else if (unlikely(res_prepare == TX_QUEUED)) { + rcu_read_unlock(); + return; } sta = tx.sta; @@ -1251,7 +1295,12 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) do { next = skb->next; skb->next = NULL; - skb_queue_tail(&local->pending[queue], skb); + if (unlikely(txpending)) + skb_queue_head(&local->pending[queue], + skb); + else + skb_queue_tail(&local->pending[queue], + skb); } while ((skb = next)); /* @@ -1276,7 +1325,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) } out: rcu_read_unlock(); - return 0; + return; drop: rcu_read_unlock(); @@ -1287,7 +1336,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) dev_kfree_skb(skb); skb = next; } - return 0; } /* device xmit handlers */ @@ -1346,7 +1394,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) FOUND_SDATA, UNKNOWN_ADDRESS, } monitor_iface = NOT_MONITOR; - int ret; if (skb->iif) odev = dev_get_by_index(&init_net, skb->iif); @@ -1360,7 +1407,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) "originating device\n", dev->name); #endif dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && @@ -1389,7 +1436,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) else if (mesh_nexthop_lookup(skb, osdata)) { dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0) IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh, @@ -1451,7 +1498,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); dev_put(odev); - return 0; + return NETDEV_TX_OK; } if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1460,10 +1507,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) u.ap); if (likely(monitor_iface != UNKNOWN_ADDRESS)) info->control.vif = &osdata->vif; - ret = ieee80211_tx(odev, skb); + + ieee80211_tx(odev, skb, false); dev_put(odev); - return ret; + return NETDEV_TX_OK; } int ieee80211_monitor_start_xmit(struct sk_buff *skb, @@ -1827,6 +1875,54 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) skb_queue_purge(&local->pending[i]); } +static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + struct ieee80211_hdr *hdr; + struct net_device *dev; + int ret; + bool result = true; + + /* does interface still exist? */ + dev = dev_get_by_index(&init_net, skb->iif); + if (!dev) { + dev_kfree_skb(skb); + return true; + } + + /* validate info->control.vif against skb->iif */ + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + + if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) { + dev_kfree_skb(skb); + result = true; + goto out; + } + + if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { + ieee80211_tx(dev, skb, true); + } else { + hdr = (struct ieee80211_hdr *)skb->data; + sta = sta_info_get(local, hdr->addr1); + + ret = __ieee80211_tx(local, &skb, sta); + if (ret != IEEE80211_TX_OK) + result = false; + } + + out: + dev_put(dev); + + return result; +} + /* * Transmit all pending packets. Called from tasklet, locks master device * TX lock so that no new packets can come in. @@ -1835,9 +1931,8 @@ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; struct net_device *dev = local->mdev; - struct ieee80211_hdr *hdr; unsigned long flags; - int i, ret; + int i; bool next; rcu_read_lock(); @@ -1868,13 +1963,8 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = skb_dequeue(&local->pending[i]); - struct sta_info *sta; - - hdr = (struct ieee80211_hdr *)skb->data; - sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta); - if (ret != IEEE80211_TX_OK) { + if (!ieee80211_tx_pending_skb(local, skb)) { skb_queue_head(&local->pending[i], skb); break; } -- cgit v1.2.3-70-g09d2 From e4e72fb4de93e3d4047a4ee3f08778422e17ed0d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2009 17:28:42 +0100 Subject: mac80211/iwlwifi: move virtual A-MDPU queue bookkeeping to iwlwifi This patch removes all the virtual A-MPDU-queue bookkeeping from mac80211. Curiously, iwlwifi already does its own bookkeeping, so it doesn't require much changes except where it needs to handle starting and stopping the queues in mac80211. To handle the queue stop/wake properly, we rewrite the software queue number for aggregation frames and internally to iwlwifi keep track of the queues that map into the same AC queue, and only talk to mac80211 about the AC queue. The implementation requires calling two new functions, iwl_stop_queue and iwl_wake_queue instead of the mac80211 counterparts. Signed-off-by: Johannes Berg Cc: Reinette Chattre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 7 ++-- drivers/net/wireless/iwlwifi/iwl-5000.c | 7 ++-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 -- drivers/net/wireless/iwlwifi/iwl-dev.h | 6 +++ drivers/net/wireless/iwlwifi/iwl-helpers.h | 52 ++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-tx.c | 8 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 1 - include/net/mac80211.h | 14 +------ net/mac80211/agg-tx.c | 44 ++-------------------- net/mac80211/ieee80211_i.h | 7 +--- net/mac80211/main.c | 9 ----- net/mac80211/sta_info.c | 12 ------ net/mac80211/sta_info.h | 2 - net/mac80211/tx.c | 19 ---------- net/mac80211/util.c | 58 ++++------------------------- 17 files changed, 84 insertions(+), 169 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index d03f5534afe..2399328e8de 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -293,7 +293,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv, if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) && (txq_id != IWL_CMD_QUEUE_NUM) && priv->mac80211_registered) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } /** diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index bd0140be774..847a6220c5e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2178,10 +2178,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, (iwl_queue_space(&txq->q) > txq->q.low_mark) && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) { if (agg->state == IWL_AGG_OFF) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); else - ieee80211_wake_queue(priv->hw, - txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); } } } else { @@ -2205,7 +2204,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark)) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } if (qc && likely(sta_id != IWL_INVALID_STATION)) diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index a3d9a95a9b3..e5ca2511a81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1295,10 +1295,9 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, (iwl_queue_space(&txq->q) > txq->q.low_mark) && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) { if (agg->state == IWL_AGG_OFF) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); else - ieee80211_wake_queue(priv->hw, - txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); } } } else { @@ -1324,7 +1323,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, if (priv->mac80211_registered && (iwl_queue_space(&txq->q) > txq->q.low_mark)) - ieee80211_wake_queue(priv->hw, txq_id); + iwl_wake_queue(priv, txq_id); } if (ieee80211_is_data_qos(tx_resp->frame_ctrl)) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 4b1298c2b0d..c54fb93e9d7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1309,9 +1309,6 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; - /* queues to support 11n aggregation */ - if (priv->cfg->sku & IWL_SKU_N) - hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues; hw->conf.beacon_int = 100; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 0baae802282..ec9a13846ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -996,6 +996,12 @@ struct iwl_priv { u8 key_mapping_key; unsigned long ucode_key_table; + /* queue refcounts */ +#define IWL_MAX_HW_QUEUES 32 + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + /* for each AC */ + atomic_t queue_stop_count[4]; + /* Indication if ieee80211_ops->open has been called */ u8 is_open; diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index fb64d297dd4..a1328c3c81a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -93,4 +93,56 @@ static inline int iwl_alloc_fw_desc(struct pci_dev *pci_dev, return (desc->v_addr != NULL) ? 0 : -ENOMEM; } +/* + * we have 8 bits used like this: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | +-+-------- AC queue (0-3) + * | | | | | | + * | +-+-+-+-+------------ HW A-MPDU queue + * | + * +---------------------- indicates agg queue + */ +static inline u8 iwl_virtual_agg_queue_num(u8 ac, u8 hwq) +{ + BUG_ON(ac > 3); /* only have 2 bits */ + BUG_ON(hwq > 31); /* only have 5 bits */ + + return 0x80 | (hwq << 2) | ac; +} + +static inline void iwl_wake_queue(struct iwl_priv *priv, u8 queue) +{ + u8 ac = queue; + u8 hwq = queue; + + if (queue & 0x80) { + ac = queue & 3; + hwq = (queue >> 2) & 0x1f; + } + + if (test_and_clear_bit(hwq, priv->queue_stopped)) + if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(priv->hw, ac); +} + +static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue) +{ + u8 ac = queue; + u8 hwq = queue; + + if (queue & 0x80) { + ac = queue & 3; + hwq = (queue >> 2) & 0x1f; + } + + if (!test_and_set_bit(hwq, priv->queue_stopped)) + if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(priv->hw, ac); +} + +#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue +#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue + #endif /* __iwl_helpers_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index b13862a598e..1f117a49c56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -763,8 +763,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) + if (info->flags & IEEE80211_TX_CTL_AMPDU) { txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; + swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id); + } priv->stations[sta_id].tid[tid].tfds_in_queue++; } @@ -895,7 +897,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); } else { - ieee80211_stop_queue(priv->hw, txq->swq_id); + iwl_stop_queue(priv, txq->swq_id); } } @@ -1433,7 +1435,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, if ((iwl_queue_space(&txq->q) > txq->q.low_mark) && priv->mac80211_registered && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) - ieee80211_wake_queue(priv->hw, txq->swq_id); + iwl_wake_queue(priv, txq->swq_id); iwl_txq_check_empty(priv, sta_id, tid, scd_flow); } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index ede29b6c4dc..a71b08ca7c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1168,7 +1168,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); } - ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb)); + iwl_stop_queue(priv, skb_get_queue_mapping(skb)); } return 0; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 55116102475..d4fdc8b7d7d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -933,7 +933,6 @@ static int __init init_mac80211_hwsim(void) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->ampdu_queues = 1; hw->flags = IEEE80211_HW_MFP_CAPABLE; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 841f7f804bb..3b83a80e3fe 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -93,12 +93,9 @@ struct ieee80211_ht_bss_info { * enum ieee80211_max_queues - maximum number of queues * * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues. - * @IEEE80211_MAX_AMPDU_QUEUES: Maximum number of queues usable - * for A-MPDU operation. */ enum ieee80211_max_queues { IEEE80211_MAX_QUEUES = 4, - IEEE80211_MAX_AMPDU_QUEUES = 16, }; /** @@ -952,12 +949,6 @@ enum ieee80211_hw_flags { * data packets. WMM/QoS requires at least four, these * queues need to have configurable access parameters. * - * @ampdu_queues: number of available hardware transmit queues - * for A-MPDU packets, these have no access parameters - * because they're used only for A-MPDU frames. Note that - * mac80211 will not currently use any of the regular queues - * for aggregation. - * * @rate_control_algorithm: rate control algorithm for this hardware. * If unset (NULL), the default algorithm will be used. Must be * set before calling ieee80211_register_hw(). @@ -982,7 +973,6 @@ struct ieee80211_hw { int vif_data_size; int sta_data_size; u16 queues; - u16 ampdu_queues; u16 max_listen_interval; s8 max_signal; u8 max_rates; @@ -1372,8 +1362,8 @@ enum ieee80211_ampdu_mlme_action { * @get_tx_stats: Get statistics of the current TX queue status. This is used * to get number of currently queued packets (queue length), maximum queue * size (limit), and total number of packets sent using each TX queue - * (count). The 'stats' pointer points to an array that has hw->queues + - * hw->ampdu_queues items. + * (count). The 'stats' pointer points to an array that has hw->queues + * items. * * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently, * this is only used for IBSS mode BSSID merging and debugging. Is not a diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 64b839bfbf1..947aaaad35d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -131,14 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, state = &sta->ampdu_mlme.tid_state_tx[tid]; - if (local->hw.ampdu_queues) { - /* - * Pretend the driver woke the queue, just in case - * it disabled it before the session was stopped. - */ - ieee80211_wake_queue( - &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); - } *state = HT_AGG_STATE_REQ_STOP_BA_MSK | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); @@ -206,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) struct sta_info *sta; struct ieee80211_sub_if_data *sdata; u8 *state; - int i, qn = -1, ret = 0; + int ret = 0; u16 start_seq_num; if (WARN_ON(!local->ops->ampdu_action)) @@ -275,29 +267,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) goto err_unlock_sta; } - if (hw->ampdu_queues) { - spin_lock(&local->queue_stop_reason_lock); - /* reserve a new queue for this session */ - for (i = 0; i < local->hw.ampdu_queues; i++) { - if (local->ampdu_ac_queue[i] < 0) { - qn = i; - local->ampdu_ac_queue[qn] = - ieee80211_ac_from_tid(tid); - break; - } - } - spin_unlock(&local->queue_stop_reason_lock); - - if (qn < 0) { -#ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "BA request denied - " - "queue unavailable for tid %d\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - ret = -ENOSPC; - goto err_unlock_sta; - } - } - /* * While we're asking the driver about the aggregation, * stop the AC queue so that we don't have to worry @@ -319,7 +288,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) tid); #endif ret = -ENOMEM; - goto err_return_queue; + goto err_wake_queue; } skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); @@ -348,7 +317,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) *state = HT_AGG_STATE_IDLE; goto err_free; } - sta->tid_to_tx_q[tid] = qn; /* Driver vetoed or OKed, but we can take packets again now */ ieee80211_wake_queue_by_reason( @@ -380,13 +348,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) err_free: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - err_return_queue: - if (qn >= 0) { - /* give queue back to pool */ - spin_lock(&local->queue_stop_reason_lock); - local->ampdu_ac_queue[qn] = -1; - spin_unlock(&local->queue_stop_reason_lock); - } + err_wake_queue: ieee80211_wake_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 32345b479ad..e6ed78cb16b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -594,12 +594,7 @@ struct ieee80211_local { const struct ieee80211_ops *ops; - /* AC queue corresponding to each AMPDU queue */ - s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES]; - unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES]; - - unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES + - IEEE80211_MAX_AMPDU_QUEUES]; + unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 756284e0bbd..a6f1d8a869b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -774,11 +774,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); - for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++) - local->ampdu_ac_queue[i] = -1; - /* using an s8 won't work with more than that */ - BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127); - sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) @@ -874,10 +869,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) */ if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES) - hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES; - if (hw->queues < 4) - hw->ampdu_queues = 0; mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), "wmaster%d", ieee80211_master_setup, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index dd3593c1fd2..c5f14e6bbde 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -203,17 +203,6 @@ void sta_info_destroy(struct sta_info *sta) if (tid_rx) tid_rx->shutdown = true; - /* - * The stop callback cannot find this station any more, but - * it didn't complete its work -- start the queue if necessary - */ - if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK && - sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK && - local->hw.ampdu_queues) - ieee80211_wake_queue_by_reason(&local->hw, - local->hw.queues + sta->tid_to_tx_q[i], - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - spin_unlock_bh(&sta->lock); /* @@ -292,7 +281,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, * enable session_timer's data differentiation. refer to * sta_rx_agg_session_timer_expired for useage */ sta->timer_to_tid[i] = i; - sta->tid_to_tx_q[i] = -1; /* rx */ sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; sta->ampdu_mlme.tid_rx[i] = NULL; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 18fd5d1a442..5534d489f50 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -206,7 +206,6 @@ struct sta_ampdu_mlme { * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers - * @tid_to_tx_q: map tid to tx queue (invalid == negative values) * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state @@ -281,7 +280,6 @@ struct sta_info { */ struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[STA_TID_NUM]; - s8 tid_to_tx_q[STA_TID_NUM]; #ifdef CONFIG_MAC80211_MESH /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 906ab785db4..3fb04a86444 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1145,25 +1145,6 @@ static int __ieee80211_tx(struct ieee80211_local *local, info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | IEEE80211_TX_CTL_FIRST_FRAGMENT); - /* - * Internally, we need to have the queue mapping point to - * the real AC queue, not the virtual A-MPDU queue. This - * now finally sets the queue to what the driver wants. - * We will later move this down into the only driver that - * needs it, iwlwifi. - */ - if (sta && local->hw.ampdu_queues && - info->flags & IEEE80211_TX_CTL_AMPDU) { - unsigned long flags; - u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); - int tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - - spin_lock_irqsave(&sta->lock, flags); - skb_set_queue_mapping(skb, local->hw.queues + - sta->tid_to_tx_q[tid]); - spin_unlock_irqrestore(&sta->lock, flags); - } - next = skb->next; len = skb->len; ret = local->ops->tx(local_to_hw(local), skb); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0247d8022f5..fdf432f1455 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -339,29 +339,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]--; - else - __clear_bit(reason, &local->queue_stop_reasons[queue]); - - if (local->queue_stop_reasons[queue] || - local->amdpu_ac_stop_refcnt[queue - hw->queues]) - return; - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; __clear_bit(reason, &local->queue_stop_reasons[queue]); @@ -400,25 +379,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, { struct ieee80211_local *local = hw_to_local(hw); - if (queue >= hw->queues) { - if (local->ampdu_ac_queue[queue - hw->queues] < 0) - return; - - /* - * for virtual aggregation queues, we need to refcount the - * internal mac80211 disable (multiple times!), keep track of - * driver disable _and_ make sure the regular queue is - * actually enabled. - */ - if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION) - local->amdpu_ac_stop_refcnt[queue - hw->queues]++; - else - __set_bit(reason, &local->queue_stop_reasons[queue]); - - /* now go on to treat the corresponding regular queue */ - queue = local->ampdu_ac_queue[queue - hw->queues]; - reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION; - } + if (WARN_ON(queue >= hw->queues)) + return; /* * Only stop if it was previously running, this is necessary @@ -474,15 +436,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues); int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) { struct ieee80211_local *local = hw_to_local(hw); - unsigned long flags; - if (queue >= hw->queues) { - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - queue = local->ampdu_ac_queue[queue - hw->queues]; - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (queue < 0) - return true; - } + if (WARN_ON(queue >= hw->queues)) + return true; return __netif_subqueue_stopped(local->mdev, queue); } @@ -497,7 +453,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < hw->queues + hw->ampdu_queues; i++) + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -- cgit v1.2.3-70-g09d2