From 0e8635a8e1f2d4a9e1bfc6c3b21419a5921e674f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 20 Jun 2009 00:53:25 +0000 Subject: net: remove NET_RX_BAD and NET_RX_CN* defines almost no users in the tree; and the few that use them treat them like NET_RX_DROP. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/decnet/dn_route.c | 2 +- net/lapb/lapb_iface.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 1d6ca8a..9383d3e 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -774,7 +774,7 @@ static int dn_rt_bug(struct sk_buff *skb) kfree_skb(skb); - return NET_RX_BAD; + return NET_RX_DROP; } static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index 2ba1bc4..bda96d1 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -407,7 +407,7 @@ int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) return lapb->callbacks.data_indication(lapb->dev, skb); kfree_skb(skb); - return NET_RX_CN_HIGH; /* For now; must be != NET_RX_DROP */ + return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */ } int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) -- cgit v1.1 From 6ed106549d17474ca17a16057f4c0ed4eba5a7ca Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 23 Jun 2009 06:03:08 +0000 Subject: net: use NETDEV_TX_OK instead of 0 in ndo_start_xmit() functions This patch is the result of an automatic spatch transformation to convert all ndo_start_xmit() return values of 0 to NETDEV_TX_OK. Some occurences are missed by the automatic conversion, those will be handled in a seperate patch. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/atm/br2684.c | 4 ++-- net/atm/clip.c | 12 ++++++------ net/atm/lec.c | 8 ++++---- net/bluetooth/bnep/netdev.c | 6 +++--- net/bridge/br_device.c | 2 +- net/ipv4/ip_gre.c | 6 +++--- net/ipv4/ipip.c | 6 +++--- net/ipv4/ipmr.c | 2 +- net/ipv6/ip6_tunnel.c | 4 ++-- net/ipv6/ip6mr.c | 2 +- net/ipv6/sit.c | 6 +++--- net/irda/irlan/irlan_eth.c | 4 ++-- net/netrom/nr_dev.c | 4 ++-- net/phonet/pep-gprs.c | 4 ++-- net/rose/rose_dev.c | 2 +- net/sched/sch_teql.c | 6 +++--- 16 files changed, 39 insertions(+), 39 deletions(-) (limited to 'net') diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 2912665f..848af11 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -238,7 +238,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) /* netif_stop_queue(dev); */ dev_kfree_skb(skb); read_unlock(&devs_lock); - return 0; + return NETDEV_TX_OK; } if (!br2684_xmit_vcc(skb, dev, brvcc)) { /* @@ -252,7 +252,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_fifo_errors++; } read_unlock(&devs_lock); - return 0; + return NETDEV_TX_OK; } /* diff --git a/net/atm/clip.c b/net/atm/clip.c index e65a3b1..64910bb 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -373,7 +373,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_ERR "clip_start_xmit: skb_dst(skb) == NULL\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } if (!skb_dst(skb)->neighbour) { #if 0 @@ -387,7 +387,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } entry = NEIGH2ENTRY(skb_dst(skb)->neighbour); if (!entry->vccs) { @@ -402,7 +402,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb); dev->stats.tx_dropped++; } - return 0; + return NETDEV_TX_OK; } pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; @@ -421,14 +421,14 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ if (old) { printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n"); - return 0; + return NETDEV_TX_OK; } dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; vcc->send(vcc, skb); if (atm_may_send(vcc, 0)) { entry->vccs->xoff = 0; - return 0; + return NETDEV_TX_OK; } spin_lock_irqsave(&clip_priv->xoff_lock, flags); netif_stop_queue(dev); /* XOFF -> throttle immediately */ @@ -440,7 +440,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) of the brief netif_stop_queue. If this isn't true or if it changes, use netif_wake_queue instead. */ spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); - return 0; + return NETDEV_TX_OK; } static int clip_mkip(struct atm_vcc *vcc, int timeout) diff --git a/net/atm/lec.c b/net/atm/lec.c index ff2e594..c463868 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -289,7 +289,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); kfree_skb(skb); if (skb2 == NULL) - return 0; + return NETDEV_TX_OK; skb = skb2; } skb_push(skb, 2); @@ -307,7 +307,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); kfree_skb(skb); if (skb2 == NULL) - return 0; + return NETDEV_TX_OK; skb = skb2; } #endif @@ -345,7 +345,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb); if (skb2 == NULL) { dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } skb = skb2; } @@ -416,7 +416,7 @@ out: if (entry) lec_arp_put(entry); dev->trans_start = jiffies; - return 0; + return NETDEV_TX_OK; } /* The inverse routine to net_open(). */ diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index d7a0e97..9c42990 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -175,14 +175,14 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_BT_BNEP_MC_FILTER if (bnep_net_mc_filter(skb, s)) { kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } #endif #ifdef CONFIG_BT_BNEP_PROTO_FILTER if (bnep_net_proto_filter(skb, s)) { kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } #endif @@ -203,7 +203,7 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); } - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops bnep_netdev_ops = { diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 18538d7..15d43ba 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -39,7 +39,7 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) else br_flood_deliver(br, skb); - return 0; + return NETDEV_TX_OK; } static int br_dev_open(struct net_device *dev) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 44e2a3d..cd85ebc 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -821,7 +821,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -889,7 +889,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -898,7 +898,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static int ipgre_tunnel_bind_dev(struct net_device *dev) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 93e2b78..98075b6 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -486,7 +486,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -524,7 +524,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -532,7 +532,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static void ipip_tunnel_bind_dev(struct net_device *dev) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 9a8da5e..06c33fb 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -212,7 +212,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) IGMPMSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops reg_vif_netdev_ops = { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 51f410e..a1d6045 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1063,14 +1063,14 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_err; t->recursion--; - return 0; + return NETDEV_TX_OK; tx_err: stats->tx_errors++; stats->tx_dropped++; kfree_skb(skb); t->recursion--; - return 0; + return NETDEV_TX_OK; } static void ip6_tnl_set_cap(struct ip6_tnl *t) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index c769f15..07ded50 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -427,7 +427,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) MRT6MSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops reg_vif_netdev_ops = { diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 68e5230..d0b8505 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -753,7 +753,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -794,7 +794,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -802,7 +802,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static void ipip6_tunnel_bind_dev(struct net_device *dev) diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 724bcf9..64230cf 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -177,7 +177,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) /* Did the realloc succeed? */ if (new_skb == NULL) - return 0; + return NETDEV_TX_OK; /* Use the new skb instead */ skb = new_skb; @@ -209,7 +209,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) self->stats.tx_bytes += skb->len; } - return 0; + return NETDEV_TX_OK; } /* diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 3513724..c7b7838 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -177,13 +177,13 @@ static int nr_xmit(struct sk_buff *skb, struct net_device *dev) if (!nr_route_frame(skb, NULL)) { kfree_skb(skb); stats->tx_errors++; - return 0; + return NETDEV_TX_OK; } stats->tx_packets++; stats->tx_bytes += len; - return 0; + return NETDEV_TX_OK; } static const struct header_ops nr_header_ops = { diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index 480839d..4667af5 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -195,7 +195,7 @@ static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) break; default: dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } skb_orphan(skb); @@ -215,7 +215,7 @@ static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); if (pep_writeable(sk)) netif_wake_queue(dev); - return 0; + return NETDEV_TX_OK; } static int gprs_set_mtu(struct net_device *dev, int new_mtu) diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 389d6e0..c711e2e 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -141,7 +141,7 @@ static int rose_xmit(struct sk_buff *skb, struct net_device *dev) } dev_kfree_skb(skb); stats->tx_errors++; - return 0; + return NETDEV_TX_OK; } static const struct header_ops rose_header_ops = { diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 9c002b6..12434b6 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -314,7 +314,7 @@ restart: netif_wake_queue(dev); txq->tx_packets++; txq->tx_bytes += length; - return 0; + return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); } @@ -323,7 +323,7 @@ restart: break; case 1: master->slaves = NEXT_SLAVE(q); - return 0; + return NETDEV_TX_OK; default: nores = 1; break; @@ -345,7 +345,7 @@ restart: drop: txq->tx_dropped++; dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) -- cgit v1.1 From 6650613d3387dcc30685e2781818ea7d0f840027 Mon Sep 17 00:00:00 2001 From: "oscar.medina@motorola.com" Date: Tue, 30 Jun 2009 03:25:39 +0000 Subject: tipc: Add socket options to get number of queued messages This patch allows a TIPC application to determine the number of messages currently waiting in a socket's receive queue (TIPC_SOCK_RECVQ_DEPTH) or in all TIPC socket receive queues (TIPC_NODE_RECVQ_DEPTH). Signed-off-by: Oscar Medina Signed-off-by: Allan Stephens Signed-off-by: David S. Miller --- net/tipc/socket.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 1848693..e8254e8 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1748,6 +1748,12 @@ static int getsockopt(struct socket *sock, value = jiffies_to_msecs(sk->sk_rcvtimeo); /* no need to set "res", since already 0 at this point */ break; + case TIPC_NODE_RECVQ_DEPTH: + value = (u32)atomic_read(&tipc_queue_size); + break; + case TIPC_SOCK_RECVQ_DEPTH: + value = skb_queue_len(&sk->sk_receive_queue); + break; default: res = -EINVAL; } -- cgit v1.1 From 1490fd89474dab9c560d3cca1a4aefbdb89be247 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 3 Jul 2009 20:11:57 +0000 Subject: net, bridge: align br_nf_ops assignment No functional change -- just for easier reading. Signed-off-by: Cyrill Gorcunov Signed-off-by: David S. Miller --- net/bridge/br_netfilter.c | 96 +++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index d22f611..4fde742 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -905,46 +905,62 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb, * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because * ip_refrag() can return NF_STOLEN. */ static struct nf_hook_ops br_nf_ops[] __read_mostly = { - { .hook = br_nf_pre_routing, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_PRE_ROUTING, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_local_in, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_LOCAL_IN, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_forward_ip, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_FORWARD, - .priority = NF_BR_PRI_BRNF - 1, }, - { .hook = br_nf_forward_arp, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_FORWARD, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_local_out, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_LOCAL_OUT, - .priority = NF_BR_PRI_FIRST, }, - { .hook = br_nf_post_routing, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_POST_ROUTING, - .priority = NF_BR_PRI_LAST, }, - { .hook = ip_sabotage_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP_PRI_FIRST, }, - { .hook = ip_sabotage_in, - .owner = THIS_MODULE, - .pf = PF_INET6, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP6_PRI_FIRST, }, + { + .hook = br_nf_pre_routing, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_PRE_ROUTING, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_local_in, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_LOCAL_IN, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_forward_ip, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_FORWARD, + .priority = NF_BR_PRI_BRNF - 1, + }, + { + .hook = br_nf_forward_arp, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_FORWARD, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_local_out, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_LOCAL_OUT, + .priority = NF_BR_PRI_FIRST, + }, + { + .hook = br_nf_post_routing, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_POST_ROUTING, + .priority = NF_BR_PRI_LAST, + }, + { + .hook = ip_sabotage_in, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_FIRST, + }, + { + .hook = ip_sabotage_in, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP6_PRI_FIRST, + }, }; #ifdef CONFIG_SYSCTL -- cgit v1.1 From ec634fe328182a1a098585bfc7b69e5042bdb08d Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 5 Jul 2009 19:23:38 -0700 Subject: net: convert remaining non-symbolic return values in ndo_start_xmit() functions This patch converts the remaining occurences of raw return values to their symbolic counterparts in ndo_start_xmit() functions that were missed by the previous automatic conversion. Additionally code that assumed the symbolic value of NETDEV_TX_OK to be zero is changed to explicitly use NETDEV_TX_OK. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/atm/mpc.c | 2 +- net/core/dev.c | 6 +++--- net/mac80211/tx.c | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/atm/mpc.c b/net/atm/mpc.c index e5bf114..1ac4b94 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -554,7 +554,7 @@ static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev) while (i < mpc->number_of_mps_macs) { if (!compare_ether_addr(eth->h_dest, (mpc->mps_macs + i*ETH_ALEN))) if ( send_via_shortcut(skb, mpc) == 0 ) /* try shortcut */ - return 0; /* success! */ + return NETDEV_TX_OK; /* success! */ i++; } diff --git a/net/core/dev.c b/net/core/dev.c index 70c27e0..dca8b50 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1704,7 +1704,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, skb_dst_drop(skb); rc = ops->ndo_start_xmit(skb, dev); - if (rc == 0) + if (rc == NETDEV_TX_OK) txq_trans_update(txq); /* * TODO: if skb_orphan() was called by @@ -1730,7 +1730,7 @@ gso: skb->next = nskb->next; nskb->next = NULL; rc = ops->ndo_start_xmit(nskb, dev); - if (unlikely(rc)) { + if (unlikely(rc != NETDEV_TX_OK)) { nskb->next = skb->next; skb->next = nskb; return rc; @@ -1744,7 +1744,7 @@ gso: out_kfree_skb: kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static u32 skb_tx_hashrnd; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d238a89..2ffb35d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1627,7 +1627,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, u32 sta_flags = 0; if (unlikely(skb->len < ETH_HLEN)) { - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1664,7 +1664,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; - ret = 0; + ret = NETDEV_TX_OK; goto fail; } memset(&mesh_hdr, 0, sizeof(mesh_hdr)); @@ -1724,7 +1724,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, hdrlen = 24; break; default: - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1766,7 +1766,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1858,10 +1858,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, dev->trans_start = jiffies; dev_queue_xmit(skb); - return 0; + return NETDEV_TX_OK; fail: - if (!ret) + if (ret == NETDEV_TX_OK) dev_kfree_skb(skb); return ret; -- cgit v1.1 From 5c91face51d29f553a444d16db4ac2c6162a3cd0 Mon Sep 17 00:00:00 2001 From: Mark Smith Date: Mon, 6 Jul 2009 11:26:24 +0000 Subject: ipv6: correct return on ipv6_rcv() packet drop The routine ipv6_rcv() uses magic number 0 for a return when it drops a packet. This corresponds to NET_RX_SUCCESS, which is obviously incorrect. Correct this by using NET_RX_DROP instead. ps. It isn't exactly clear who the IPv6 maintainers are, apologies if I've missed any. Signed-off-by: Mark Smith Signed-off-by: David S. Miller --- net/ipv6/ip6_input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6d6a427..2d9cbaa 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -63,7 +63,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); - return 0; + return NET_RX_DROP; } rcu_read_lock(); @@ -133,7 +133,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (ipv6_parse_hopopts(skb) < 0) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); rcu_read_unlock(); - return 0; + return NET_RX_DROP; } } @@ -149,7 +149,7 @@ err: drop: rcu_read_unlock(); kfree_skb(skb); - return 0; + return NET_RX_DROP; } /* -- cgit v1.1 From 482d804cb4b520b6e3134c959c968712ebcdea02 Mon Sep 17 00:00:00 2001 From: Mark Smith Date: Mon, 6 Jul 2009 11:05:58 +0000 Subject: econet: use NET_RX_SUCCESS instead of magic number 0 for econet_rcv successful return Signed-off-by: Mark Smith Signed-off-by: David S. Miller --- net/econet/af_econet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 2e1f836..e114da7 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -1072,7 +1072,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet skb->protocol = htons(ETH_P_IP); skb_pull(skb, sizeof(struct ec_framehdr)); netif_rx(skb); - return 0; + return NET_RX_SUCCESS; } sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net); @@ -1083,7 +1083,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet hdr->port)) goto drop; - return 0; + return NET_RX_SUCCESS; drop: kfree_skb(skb); -- cgit v1.1 From bff38771e1065c7fc3de87e47ba366151eea573c Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 8 Jul 2009 11:10:56 -0700 Subject: netpoll: Introduce netpoll_carrier_timeout kernel option Some PHYs require longer timeouts for carrier detection, and auto-negotiation process may take indefinite amount of time. It may be inconvenient to force longer timeouts for sane PHYs, so let's introduce a kernel command line option. Since we're using module_param(), the option also can be changed in runtime. Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- net/core/netpoll.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 9675f31..3afe381 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -9,6 +9,7 @@ * Copyright (C) 2002 Red Hat, Inc. */ +#include #include #include #include @@ -50,6 +51,9 @@ static atomic_t trapped; static void zap_completion_queue(void); static void arp_reply(struct sk_buff *skb); +static unsigned int carrier_timeout = 4; +module_param(carrier_timeout, uint, 0644); + static void queue_process(struct work_struct *work) { struct netpoll_info *npinfo = @@ -732,7 +736,7 @@ int netpoll_setup(struct netpoll *np) } atleast = jiffies + HZ/10; - atmost = jiffies + 4*HZ; + atmost = jiffies + carrier_timeout * HZ; while (!netif_carrier_ok(ndev)) { if (time_after(jiffies, atmost)) { printk(KERN_NOTICE -- cgit v1.1 From e36d56b64808aec54b68b4e9976180c1da0933b2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 9 Jun 2009 21:04:43 +0200 Subject: cfg80211: pass netdev to change_virtual_intf If there was a reason I'm passing the ifidx I cannot remember it any more and don't see one now, so let's just pass the pointer itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 9 ++------- net/wireless/nl80211.c | 12 +++++------- net/wireless/wext-compat.c | 2 +- 3 files changed, 8 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3f47276..eb93eb6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -74,19 +74,14 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) return 0; } -static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, +static int ieee80211_change_iface(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct net_device *dev; struct ieee80211_sub_if_data *sdata; int ret; - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - if (!nl80211_type_check(type)) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 43bdb13..f91e5d4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -767,7 +767,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; struct vif_params params; - int err, ifindex; + int err; enum nl80211_iftype otype, ntype; struct net_device *dev; u32 _flags, *flags = NULL; @@ -781,9 +781,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (err) goto unlock_rtnl; - ifindex = dev->ifindex; otype = ntype = dev->ieee80211_ptr->iftype; - dev_put(dev); if (info->attrs[NL80211_ATTR_IFTYPE]) { ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); @@ -826,20 +824,20 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, + err = drv->ops->change_virtual_intf(&drv->wiphy, dev, ntype, flags, ¶ms); else err = 0; - dev = __dev_get_by_index(&init_net, ifindex); - WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype)); + WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - if (dev && !err && (ntype != otype)) { + if (!err && (ntype != otype)) { if (otype == NL80211_IFTYPE_ADHOC) cfg80211_clear_ibss(dev, false); } unlock: + dev_put(dev); cfg80211_put_dev(drv); unlock_rtnl: rtnl_unlock(); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index d030c53..9e56f35 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -103,7 +103,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, + ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev, type, NULL, &vifparams); WARN_ON(!ret && wdev->iftype != type); -- cgit v1.1 From a538e2d5a30f577e9c8f6ccfe72b29a258e0fe86 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 16 Jun 2009 19:56:42 +0200 Subject: cfg80211: issue netlink notification when scan starts To ease multiple apps working together smoothly, send a notification when a scan is started. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 39 ++++++++++++++++++++++++++++++--------- net/wireless/nl80211.h | 2 ++ net/wireless/scan.c | 3 ++- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f91e5d4..5a6c4ca 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2839,6 +2839,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) drv->scan_req = request; err = drv->ops->scan(&drv->wiphy, dev, request); + if (!err) + nl80211_send_scan_start(drv, dev); + out_free: if (err) { drv->scan_req = NULL; @@ -3665,11 +3668,11 @@ static int nl80211_add_scan_req(struct sk_buff *msg, return -ENOBUFS; } -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) +static int nl80211_send_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, + u32 cmd) { void *hdr; @@ -3690,6 +3693,24 @@ static int nl80211_send_scan_donemsg(struct sk_buff *msg, return -EMSGSIZE; } +void nl80211_send_scan_start(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_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_TRIGGER_SCAN) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct net_device *netdev) { @@ -3699,8 +3720,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_NEW_SCAN_RESULTS) < 0) { + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_NEW_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; } @@ -3717,8 +3738,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCAN_ABORTED) < 0) { + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCAN_ABORTED) < 0) { nlmsg_free(msg); return; } diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5c12ad1..a3872a4 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -6,6 +6,8 @@ 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_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev); 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, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index f8e71b3..261a063 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -647,7 +647,8 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (err) { rdev->scan_req = NULL; kfree(creq); - } + } else + nl80211_send_scan_start(rdev, dev); out: cfg80211_put_dev(rdev); return err; -- cgit v1.1 From f1d58c2521eb160178b2151d6326d8dc5d7c8560 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 13:13:00 +0200 Subject: mac80211: push rx status into skb->cb Within mac80211, we often need to copy the rx status into skb->cb. This is wasteful, as drivers could be building it in there to start with. This patch changes the API so that drivers are expected to pass the RX status in skb->cb, now accessible as IEEE80211_SKB_RXCB(skb). It also updates all drivers to pass the rx status in there, but only by making them memcpy() it into place before the call to the receive function (ieee80211_rx(_irqsafe)). Each driver can now be optimised on its own schedule. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 6 ++-- net/mac80211/ieee80211_i.h | 10 ++----- net/mac80211/main.c | 5 +--- net/mac80211/mesh.c | 6 ++-- net/mac80211/mesh.h | 3 +- net/mac80211/mlme.c | 4 +-- net/mac80211/rx.c | 71 ++++++++++++++++++++-------------------------- net/mac80211/scan.c | 4 +-- 8 files changed, 43 insertions(+), 66 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 0b30277..15d5a53 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -705,7 +705,7 @@ static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; u16 fc; - rx_status = (struct ieee80211_rx_status *) skb->cb; + rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); @@ -836,8 +836,7 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) } ieee80211_rx_result -ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; @@ -852,7 +851,6 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_AUTH: skb_queue_tail(&sdata->u.ibss.skb_queue, skb); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 68eb505..c65c65a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -943,8 +943,7 @@ extern const struct iw_handler_def ieee80211_iw_handler_def; /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); + struct sk_buff *skb); int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata); int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len); int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len); @@ -967,8 +966,7 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result -ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); +ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 *addr, u32 supp_rates); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, @@ -988,9 +986,7 @@ int ieee80211_scan_results(struct ieee80211_local *local, char *buf, size_t len); void ieee80211_scan_cancel(struct ieee80211_local *local); ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); +ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, const char *ie, size_t len); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 092a017..5b69f5f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -330,19 +330,16 @@ static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sk_buff *skb; - struct ieee80211_rx_status rx_status; struct ieee80211_ra_tid *ra_tid; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { switch (skb->pkt_type) { case IEEE80211_RX_MSG: - /* status is in skb->cb */ - memcpy(&rx_status, skb->cb, sizeof(rx_status)); /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; - __ieee80211_rx(local_to_hw(local), skb, &rx_status); + ieee80211_rx(local_to_hw(local), skb); break; case IEEE80211_TX_STATUS_MSG: skb->pkt_type = 0; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 11cf45b..542ea02 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -568,7 +568,7 @@ static void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ifmsh = &sdata->u.mesh; - rx_status = (struct ieee80211_rx_status *) skb->cb; + rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; @@ -671,8 +671,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) } ieee80211_rx_result -ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; @@ -689,7 +688,6 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: case IEEE80211_STYPE_ACTION: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); skb_queue_tail(&ifmsh->skb_queue, skb); queue_work(local->hw.workqueue, &ifmsh->work); return RX_QUEUED; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c7d7281..2a2ed18 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -208,8 +208,7 @@ void ieee80211s_init(void); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result -ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); +ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aca22b0..5e25d32 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2063,8 +2063,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; @@ -2080,7 +2079,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); case IEEE80211_STYPE_AUTH: case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index de5bba7..0563b69 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -30,7 +30,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *skb, - struct ieee80211_rx_status *status, u16 mpdu_seq_num, int bar_req); /* @@ -59,11 +58,11 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, return skb; } -static inline int should_drop_frame(struct ieee80211_rx_status *status, - struct sk_buff *skb, +static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len, int radiotap_len) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) @@ -111,10 +110,10 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, static void ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate, int rtap_len) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_header *rthdr; unsigned char *pos; @@ -220,9 +219,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, */ static struct sk_buff * ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; int needed_headroom = 0; struct sk_buff *skb, *skb2; @@ -248,8 +247,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, present_fcs_len = FCS_LEN; if (!local->monitors) { - if (should_drop_frame(status, origskb, present_fcs_len, - rtap_len)) { + if (should_drop_frame(origskb, present_fcs_len, rtap_len)) { dev_kfree_skb(origskb); return NULL; } @@ -257,7 +255,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return remove_monitor_info(local, origskb, rtap_len); } - if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) { + if (should_drop_frame(origskb, present_fcs_len, rtap_len)) { /* only need to expand headroom if necessary */ skb = origskb; origskb = NULL; @@ -289,7 +287,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, /* if necessary, prepend radiotap information */ if (!(status->flag & RX_FLAG_RADIOTAP)) - ieee80211_add_rx_radiotap_header(local, skb, status, rate, + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); skb_reset_mac_header(skb); @@ -421,12 +419,11 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) struct sk_buff *skb = rx->skb; if (unlikely(local->hw_scanning)) - return ieee80211_scan_rx(rx->sdata, skb, rx->status); + return ieee80211_scan_rx(rx->sdata, skb); if (unlikely(local->sw_scanning)) { /* drop all the other packets during a software scan anyway */ - if (ieee80211_scan_rx(rx->sdata, skb, rx->status) - != RX_QUEUED) + if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) dev_kfree_skb(skb); return RX_QUEUED; } @@ -1620,7 +1617,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) /* manage reordering buffer according to requested */ /* sequence number */ rcu_read_lock(); - ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, NULL, + ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, start_seq_num, 1); rcu_read_unlock(); return RX_DROP_UNUSABLE; @@ -1817,13 +1814,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; if (ieee80211_vif_is_mesh(&sdata->vif)) - return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_mesh_rx_mgmt(sdata, rx->skb); if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_ibss_rx_mgmt(sdata, rx->skb); if (sdata->vif.type == NL80211_IFTYPE_STATION) - return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_sta_rx_mgmt(sdata, rx->skb); return RX_DROP_MONITOR; } @@ -2114,9 +2111,9 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, */ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr; @@ -2227,20 +2224,21 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw, { struct ieee80211_supported_band *sband; struct ieee80211_rate *rate; - struct ieee80211_rx_status status; + struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; + struct ieee80211_rx_status *status; - if (!tid_agg_rx->reorder_buf[index]) + if (!skb) goto no_frame; + status = IEEE80211_SKB_RXCB(skb); + /* release the reordered frames to stack */ - memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status)); - sband = hw->wiphy->bands[status.band]; - if (status.flag & RX_FLAG_HT) + sband = hw->wiphy->bands[status->band]; + if (status->flag & RX_FLAG_HT) rate = sband->bitrates; /* TODO: HT rates */ else - rate = &sband->bitrates[status.rate_idx]; - __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], - &status, rate); + rate = &sband->bitrates[status->rate_idx]; + __ieee80211_rx_handle_packet(hw, skb, rate); tid_agg_rx->stored_mpdu_num--; tid_agg_rx->reorder_buf[index] = NULL; @@ -2265,7 +2263,6 @@ no_frame: static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *skb, - struct ieee80211_rx_status *rxstatus, u16 mpdu_seq_num, int bar_req) { @@ -2324,8 +2321,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, /* put the frame in the reordering buffer */ tid_agg_rx->reorder_buf[index] = skb; tid_agg_rx->reorder_time[index] = jiffies; - memcpy(tid_agg_rx->reorder_buf[index]->cb, rxstatus, - sizeof(*rxstatus)); tid_agg_rx->stored_mpdu_num++; /* release the buffer until next missing frame */ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) @@ -2374,8 +2369,7 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, } static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, - struct sk_buff *skb, - struct ieee80211_rx_status *status) + struct sk_buff *skb) { struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -2424,7 +2418,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, /* according to mpdu sequence number deal with reordering buffer */ mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; - ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, status, + ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, mpdu_seq_num, 0); end_reorder: return ret; @@ -2434,12 +2428,12 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status) +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate = NULL; struct ieee80211_supported_band *sband; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); if (status->band < 0 || status->band >= IEEE80211_NUM_BANDS) { @@ -2482,7 +2476,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, * if it was previously present. * Also, frames with less than 16 bytes are dropped. */ - skb = ieee80211_rx_monitor(local, skb, status, rate); + skb = ieee80211_rx_monitor(local, skb, rate); if (!skb) { rcu_read_unlock(); return; @@ -2500,8 +2494,8 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, * frames from other than operational channel), but that should not * happen in normal networks. */ - if (!ieee80211_rx_reorder_ampdu(local, skb, status)) - __ieee80211_rx_handle_packet(hw, skb, status, rate); + if (!ieee80211_rx_reorder_ampdu(local, skb)) + __ieee80211_rx_handle_packet(hw, skb, rate); rcu_read_unlock(); } @@ -2509,16 +2503,13 @@ EXPORT_SYMBOL(__ieee80211_rx); /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ -void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status) +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); skb->dev = local->mdev; - /* copy status into skb->cb for use by tasklet */ - memcpy(skb->cb, status, sizeof(*status)); skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 2a8d09a..8b2416c 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -135,9 +135,9 @@ void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid, } ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_mgmt *mgmt; struct ieee80211_bss *bss; u8 *elements; -- cgit v1.1 From 386aa23dd5f94b810210b9f849807946a5100b57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 17:28:45 +0200 Subject: mac80211: improve per-sta debugfs We had code for a number of files, that we didn't publish in debugfs, fix that. Also make the agg_status file layout more readable and add more information to it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/debugfs_sta.c | 98 +++++++++++++++++++++++++++++----------------- net/mac80211/sta_info.h | 17 ++++++++ 2 files changed, 79 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 90230c7..33a2e89 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -120,45 +120,38 @@ STA_OPS(last_seq_ctrl); static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[768], *p = buf; + char buf[30 + STA_TID_NUM * 70], *p = buf; int i; struct sta_info *sta = file->private_data; - p += scnprintf(p, sizeof(buf)+buf-p, "Agg state for STA is:\n"); - p += scnprintf(p, sizeof(buf)+buf-p, " STA next dialog_token is %d \n " - "TIDs info is: \n TID :", - (sta->ampdu_mlme.dialog_token_allocator + 1)); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", i); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n RX :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_rx[i]); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_rx[i] ? - sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i]); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i] ? - sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i] ? - sta->ampdu_mlme.tid_tx[i]->ssn : 0); - p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + spin_lock_bh(&sta->lock); + p += scnprintf(p, sizeof(buf)+buf-p, "next dialog_token is %#02x\n", + sta->ampdu_mlme.dialog_token_allocator + 1); + for (i = 0; i < STA_TID_NUM; i++) { + p += scnprintf(p, sizeof(buf)+buf-p, "TID %02d:", i); + p += scnprintf(p, sizeof(buf)+buf-p, " RX=%x", + sta->ampdu_mlme.tid_state_rx[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x", + sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x", + sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_rx[i]->ssn : 0); + + p += scnprintf(p, sizeof(buf)+buf-p, " TX=%x", + sta->ampdu_mlme.tid_state_tx[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x", + sta->ampdu_mlme.tid_state_tx[i] ? + sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x", + sta->ampdu_mlme.tid_state_tx[i] ? + sta->ampdu_mlme.tid_tx[i]->ssn : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/pending=%03d", + sta->ampdu_mlme.tid_state_tx[i] ? + skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + } + spin_unlock_bh(&sta->lock); return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } @@ -203,6 +196,22 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(inactive_ms); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); + DEBUGFS_ADD(dev); + DEBUGFS_ADD(rx_packets); + DEBUGFS_ADD(tx_packets); + DEBUGFS_ADD(rx_bytes); + DEBUGFS_ADD(tx_bytes); + DEBUGFS_ADD(rx_duplicates); + DEBUGFS_ADD(rx_fragments); + DEBUGFS_ADD(rx_dropped); + DEBUGFS_ADD(tx_fragments); + DEBUGFS_ADD(tx_filtered); + DEBUGFS_ADD(tx_retry_failed); + DEBUGFS_ADD(tx_retry_count); + DEBUGFS_ADD(last_signal); + DEBUGFS_ADD(last_qual); + DEBUGFS_ADD(last_noise); + DEBUGFS_ADD(wep_weak_iv_count); } void ieee80211_sta_debugfs_remove(struct sta_info *sta) @@ -212,6 +221,23 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta) DEBUGFS_DEL(inactive_ms); DEBUGFS_DEL(last_seq_ctrl); DEBUGFS_DEL(agg_status); + DEBUGFS_DEL(aid); + DEBUGFS_DEL(dev); + DEBUGFS_DEL(rx_packets); + DEBUGFS_DEL(tx_packets); + DEBUGFS_DEL(rx_bytes); + DEBUGFS_DEL(tx_bytes); + DEBUGFS_DEL(rx_duplicates); + DEBUGFS_DEL(rx_fragments); + DEBUGFS_DEL(rx_dropped); + DEBUGFS_DEL(tx_fragments); + DEBUGFS_DEL(tx_filtered); + DEBUGFS_DEL(tx_retry_failed); + DEBUGFS_DEL(tx_retry_count); + DEBUGFS_DEL(last_signal); + DEBUGFS_DEL(last_qual); + DEBUGFS_DEL(last_noise); + DEBUGFS_DEL(wep_weak_iv_count); debugfs_remove(sta->debugfs.dir); sta->debugfs.dir = NULL; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 49a1a1f..4ecf10a 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -308,6 +308,23 @@ struct sta_info { struct dentry *inactive_ms; struct dentry *last_seq_ctrl; struct dentry *agg_status; + struct dentry *aid; + struct dentry *dev; + struct dentry *rx_packets; + struct dentry *tx_packets; + struct dentry *rx_bytes; + struct dentry *tx_bytes; + struct dentry *rx_duplicates; + struct dentry *rx_fragments; + struct dentry *rx_dropped; + struct dentry *tx_fragments; + struct dentry *tx_filtered; + struct dentry *tx_retry_failed; + struct dentry *tx_retry_count; + struct dentry *last_signal; + struct dentry *last_qual; + struct dentry *last_noise; + struct dentry *wep_weak_iv_count; bool add_has_run; } debugfs; #endif -- cgit v1.1 From 83f5e2cf79390f7ca909b7035dd415230257cc4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 17:41:49 +0200 Subject: cfg80211: prohibit scanning the same channel more than once It isn't very useful to scan the same channel more than once during a given scan, and some hardware (notably iwlwifi) can only scan a limited number of channels at a time. To prevent any overflows, simply disallow scanning any channel multiple times in a given scan command. This is a small change in the userspace ABI, but the only user, wpa_supplicant, never asks for a scan with the same frequency listed twice. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5a6c4ca..7946b82 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2695,6 +2695,31 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return r; } +static int validate_scan_freqs(struct nlattr *freqs) +{ + struct nlattr *attr1, *attr2; + int n_channels = 0, tmp1, tmp2; + + nla_for_each_nested(attr1, freqs, tmp1) { + n_channels++; + /* + * Some hardware has a limited channel list for + * scanning, and it is pretty much nonsensical + * to scan for a channel twice, so disallow that + * and don't require drivers to check that the + * channel list they get isn't longer than what + * they can scan, as long as they can scan all + * the channels they registered at once. + */ + nla_for_each_nested(attr2, freqs, tmp2) + if (attr1 != attr2 && + nla_get_u32(attr1) == nla_get_u32(attr2)) + return 0; + } + + return n_channels; +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -2704,7 +2729,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) struct ieee80211_channel *channel; struct nlattr *attr; struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_channels = 0, i; + int err, tmp, n_ssids = 0, n_channels, i; enum ieee80211_band band; size_t ie_len; @@ -2735,13 +2760,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) - n_channels++; + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); if (!n_channels) { err = -EINVAL; goto out; } } else { + n_channels = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; -- cgit v1.1 From 3daf097594d398b2ddd2dca6caeaa92cb9148e23 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 18 Jun 2009 23:21:14 +0100 Subject: cfg80211: Advertise ciphers via WE according to driver capability Signed-off-by: David Kilroy Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 9e56f35..7dbe6c6 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -204,8 +204,19 @@ int cfg80211_wext_giwrange(struct net_device *dev, 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; + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2; + + for (c = 0; c < wdev->wiphy->n_cipher_suites; c++) { + switch (wdev->wiphy->cipher_suites[c]) { + case WLAN_CIPHER_SUITE_TKIP: + range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + break; + + case WLAN_CIPHER_SUITE_CCMP: + range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; + break; + } + } for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { int i; -- cgit v1.1 From 51cd4aabd082144881644c308647ca501690b68c Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 18 Jun 2009 23:21:15 +0100 Subject: cfg80211: allow drivers that can't scan for specific ssids Signed-off-by: David Kilroy Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 3 --- net/wireless/wext-compat.c | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index d585029..a98670c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -346,9 +346,6 @@ 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; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 7dbe6c6..bb97061 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -247,7 +247,8 @@ int cfg80211_wext_giwrange(struct net_device *dev, IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - range->scan_capa |= IW_SCAN_CAPA_ESSID; + if (wdev->wiphy->max_scan_ssids > 0) + range->scan_capa |= IW_SCAN_CAPA_ESSID; return 0; } -- cgit v1.1 From 2ab658f9ce218ae93b3d2db2b3fe68bfefb81196 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 18 Jun 2009 23:21:16 +0100 Subject: cfg80211: set WE encoding size based on available ciphers Only set the sizes for WEP40 and WEP104. Signed-off-by: David Kilroy Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index bb97061..2f7e776 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -173,9 +173,6 @@ int cfg80211_wext_giwrange(struct net_device *dev, 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; @@ -215,6 +212,16 @@ int cfg80211_wext_giwrange(struct net_device *dev, case WLAN_CIPHER_SUITE_CCMP: range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; break; + + case WLAN_CIPHER_SUITE_WEP40: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP40; + break; + + case WLAN_CIPHER_SUITE_WEP104: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP104; + break; } } -- cgit v1.1 From 27bea66c22582853ad2e1de93d26c3016493818b Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 18 Jun 2009 23:21:17 +0100 Subject: cfg80211: infer WPA and WPA2 support from TKIP and CCMP Signed-off-by: David Kilroy Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 2f7e776..cae3b52 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -201,16 +201,16 @@ int cfg80211_wext_giwrange(struct net_device *dev, 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; - for (c = 0; c < wdev->wiphy->n_cipher_suites; c++) { switch (wdev->wiphy->cipher_suites[c]) { case WLAN_CIPHER_SUITE_TKIP: - range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | + IW_ENC_CAPA_WPA); break; case WLAN_CIPHER_SUITE_CCMP: - range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; + range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | + IW_ENC_CAPA_WPA2); break; case WLAN_CIPHER_SUITE_WEP40: -- cgit v1.1 From f58d4ed98bfe7b2febcd6f0d62744b623e4b8371 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 19 Jun 2009 02:45:21 +0200 Subject: cfg80211: send wext MLME-MICHAELMICFAILURE.indication Instead of having mac80211 do it itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/event.c | 18 +----------------- net/wireless/mlme.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/event.c b/net/mac80211/event.c index f288d01..3ac6362 100644 --- a/net/mac80211/event.c +++ b/net/mac80211/event.c @@ -7,8 +7,7 @@ * * mac80211 - events */ - -#include +#include #include "ieee80211_i.h" /* @@ -19,21 +18,6 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, struct ieee80211_hdr *hdr, const u8 *tsc) { - union iwreq_data wrqu; - char *buf = kmalloc(128, GFP_ATOMIC); - - if (buf) { - /* TODO: needed parameters: count, key type, TSC */ - sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" - "keyid=%d %scast addr=%pM)", - keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", - hdr->addr2); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = strlen(buf); - wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); - kfree(buf); - } - cfg80211_michael_mic_failure(sdata->dev, hdr->addr2, (hdr->addr1[0] & 0x01) ? NL80211_KEYTYPE_GROUP : diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 4218436..e56bbea 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -77,6 +77,22 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; + char *buf = kmalloc(128, GFP_ATOMIC); + + if (buf) { + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" + "keyid=%d %scast addr=%pM)", key_id, + key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", + addr); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); + } +#endif + nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); -- cgit v1.1 From df2b35b65b7142bac2c7add3a1dedde3a373aff3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:42 +0200 Subject: wext: allow returning NULL stats Currently, wext drivers cannot return NULL for stats even though that would make the ioctl return -EOPNOTSUPP because that would mean they are no longer listed in /proc/net/wireless. This patch changes the wext core's behaviour to list them if they have any wireless_handlers, but only show their stats when available, so that drivers can start returning NULL if stats are currently not available, reducing confusion for e.g. IBSS. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 252c201..5da07e0 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -610,6 +610,11 @@ static void wireless_seq_printf_stats(struct seq_file *seq, { /* Get stats from the driver */ struct iw_statistics *stats = get_wireless_stats(dev); + static struct iw_statistics nullstats = {}; + + /* show device if it's wireless regardless of current stats */ + if (!stats && dev->wireless_handlers) + stats = &nullstats; if (stats) { seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " @@ -628,7 +633,9 @@ static void wireless_seq_printf_stats(struct seq_file *seq, stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + + if (stats != &nullstats) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; } } -- cgit v1.1 From d3cebbdcedf8d1519913f6e9bf88dbac7fbaa760 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:43 +0200 Subject: mac80211: fix todo lock The key todo lock can be taken from different locks that require it to be _bh to avoid lock inversion due to (soft)irqs. This should fix the two problems reported by Bob and Gabor: http://mid.gmane.org/20090619113049.GB18956@hash.localnet http://mid.gmane.org/4A3FA376.8020307@openwrt.org Signed-off-by: Johannes Berg Cc: Bob Copeland Cc: Gabor Juhos Signed-off-by: John W. Linville --- net/mac80211/key.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ce26756..659a42d 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -67,6 +67,8 @@ static DECLARE_WORK(todo_work, key_todo); * * @key: key to add to do item for * @flag: todo flag(s) + * + * Must be called with IRQs or softirqs disabled. */ static void add_todo(struct ieee80211_key *key, u32 flag) { @@ -140,9 +142,9 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf); if (!ret) { - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) @@ -164,12 +166,12 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) if (!key || !key->local->ops->set_key) return; - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); return; } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); sta = get_sta_for_key(key); sdata = key->sdata; @@ -188,9 +190,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) wiphy_name(key->local->hw.wiphy), key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, @@ -437,14 +439,14 @@ void ieee80211_key_link(struct ieee80211_key *key, __ieee80211_key_replace(sdata, sta, old_key, key); - spin_unlock_irqrestore(&sdata->local->key_lock, flags); - /* free old key later */ add_todo(old_key, KEY_FLAG_TODO_DELETE); add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); if (netif_running(sdata->dev)) add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD); + + spin_unlock_irqrestore(&sdata->local->key_lock, flags); } static void __ieee80211_key_free(struct ieee80211_key *key) @@ -547,7 +549,7 @@ static void __ieee80211_key_todo(void) */ synchronize_rcu(); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); while (!list_empty(&todo_list)) { key = list_first_entry(&todo_list, struct ieee80211_key, todo); list_del_init(&key->todo); @@ -558,7 +560,7 @@ static void __ieee80211_key_todo(void) KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); key->flags &= ~todoflags; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); work_done = false; @@ -591,9 +593,9 @@ static void __ieee80211_key_todo(void) WARN_ON(!work_done); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } void ieee80211_key_todo(void) -- cgit v1.1 From 898324025fc12132d3ed98f8baf4fb3fa45327e0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:44 +0200 Subject: wext: default to y The way I initially thought we could do wireless extensions is by making all the compat code in cfg80211 be independent of CONFIG_WIRELESS_EXT, but this is turning out to not be feasible. Therefore, fix the Kconfig help text and make the option default to yes, so people won't get a nasty surprise when mac80211 will get rid of its 'select WIRELESS_EXT' any time now. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/Kconfig | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 4428dd5..ec64571 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -35,19 +35,13 @@ config WIRELESS_OLD_REGULATORY config WIRELESS_EXT bool "Wireless extensions" - default n + default y ---help--- This option enables the legacy wireless extensions (wireless network interface configuration via ioctls.) - Wireless extensions will be replaced by cfg80211 and - will be required only by legacy drivers that implement - wireless extension handlers. This option does not - affect the wireless-extension backward compatibility - code in cfg80211. - - Say N (if you can) unless you know you need wireless - extensions for external modules. + Say Y unless you've upgraded all your userspace to use + nl80211 instead of wireless extensions. config WIRELESS_EXT_SYSFS bool "Wireless extensions sysfs files" -- cgit v1.1 From 2a783c136b4f280d9863170bd6703d25bdb4746c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:45 +0200 Subject: cfg80211: move break statement to correct place Move a break statement to the correct place _after_ the #endif, otherwise w/o WIRELESS_EXT things break badly. Also, while touching this code, do a cleanup and assign dev->ieee80211_ptr to a new variable. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index a98670c..d41b741 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -526,52 +526,53 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, void *ndev) { struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; - if (!dev->ieee80211_ptr) + if (!wdev) return NOTIFY_DONE; - rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + rdev = wiphy_to_dev(wdev->wiphy); - WARN_ON(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_UNSPECIFIED); + WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); switch (state) { case NETDEV_REGISTER: mutex_lock(&rdev->devlist_mtx); - list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list); + list_add(&wdev->list, &rdev->netdev_list); if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { printk(KERN_ERR "wireless: failed to add phy80211 " "symlink to netdev!\n"); } - dev->ieee80211_ptr->netdev = dev; + wdev->netdev = dev; #ifdef CONFIG_WIRELESS_EXT - dev->ieee80211_ptr->wext.default_key = -1; - dev->ieee80211_ptr->wext.default_mgmt_key = -1; + wdev->wext.default_key = -1; + wdev->wext.default_mgmt_key = -1; #endif mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_GOING_DOWN: - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + if (wdev->iftype != NL80211_IFTYPE_ADHOC) break; - if (!dev->ieee80211_ptr->ssid_len) + if (!wdev->ssid_len) break; cfg80211_leave_ibss(rdev, dev, true); break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + if (wdev->iftype != NL80211_IFTYPE_ADHOC) break; - if (!dev->ieee80211_ptr->wext.ibss.ssid_len) + if (!wdev->wext.ibss.ssid_len) break; - cfg80211_join_ibss(rdev, dev, &dev->ieee80211_ptr->wext.ibss); - break; + cfg80211_join_ibss(rdev, dev, &wdev->wext.ibss); #endif + break; case NETDEV_UNREGISTER: mutex_lock(&rdev->devlist_mtx); - if (!list_empty(&dev->ieee80211_ptr->list)) { + if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); - list_del_init(&dev->ieee80211_ptr->list); + list_del_init(&wdev->list); } mutex_unlock(&rdev->devlist_mtx); break; -- cgit v1.1 From dad823302135a2d99efd40e35d94a6ff14961c93 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:46 +0200 Subject: nl80211: clean up function definitions I don't like the 'extern' keyword much when it's not necessary, it makes lines rather long. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.h | 58 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index a3872a4..cf0d271 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -3,41 +3,41 @@ #include "core.h" -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_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -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); -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_deauth(struct cfg80211_registered_device *rdev, +int nl80211_init(void); +void nl80211_exit(void); +void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +void nl80211_send_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len); +void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *addr); +void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr); -extern void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr); -extern void + const u8 *addr); +void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, const u8 *tsc); -extern void +void nl80211_send_beacon_hint_event(struct wiphy *wiphy, struct ieee80211_channel *channel_before, struct ieee80211_channel *channel_after); -- cgit v1.1 From e6d6e3420d511cd7552a95d1f04bd4c80a9ddb34 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:47 +0200 Subject: cfg80211: use proper allocation flags Instead of hardcoding GFP_ATOMIC everywhere, add a new function parameter that gets the flags from the caller. Obviously then I need to update all callers (all of them in mac80211), and it turns out that now it's ok to use GFP_KERNEL in almost all places. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/event.c | 5 +++-- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/mlme.c | 30 ++++++++++++++++++----------- net/mac80211/rx.c | 3 ++- net/mac80211/wpa.c | 3 ++- net/wireless/mlme.c | 30 ++++++++++++++--------------- net/wireless/nl80211.c | 48 ++++++++++++++++++++++++++-------------------- net/wireless/nl80211.h | 14 +++++++------- 8 files changed, 77 insertions(+), 59 deletions(-) (limited to 'net') diff --git a/net/mac80211/event.c b/net/mac80211/event.c index 3ac6362..01ae759 100644 --- a/net/mac80211/event.c +++ b/net/mac80211/event.c @@ -16,11 +16,12 @@ * driver or is still in the frame), it should provide that information. */ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, - struct ieee80211_hdr *hdr, const u8 *tsc) + struct ieee80211_hdr *hdr, const u8 *tsc, + gfp_t gfp) { cfg80211_michael_mic_failure(sdata->dev, hdr->addr2, (hdr->addr1[0] & 0x01) ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE, - keyidx, tsc); + keyidx, tsc, gfp); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c65c65a..e0323e5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1088,7 +1088,8 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, int rate, int erp, int short_preamble); void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, - struct ieee80211_hdr *hdr, const u8 *tsc); + struct ieee80211_hdr *hdr, const u8 *tsc, + gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int encrypt); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5e25d32..383392b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -419,9 +419,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len, + GFP_KERNEL); else - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len, + GFP_KERNEL); ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1006,7 +1008,8 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) sdata->dev->name, ifmgd->bssid); ifmgd->state = IEEE80211_STA_MLME_DISABLED; ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid); + cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid, + GFP_KERNEL); /* * Most likely AP is not in the range so remove the @@ -1055,7 +1058,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) sdata->dev->name, ifmgd->bssid); ifmgd->state = IEEE80211_STA_MLME_DISABLED; ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid); + cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid, + GFP_KERNEL); ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); @@ -1243,7 +1247,8 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) sdata->dev->name, ifmgd->bssid); ifmgd->state = IEEE80211_STA_MLME_DISABLED; ieee80211_recalc_idle(local); - cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid); + cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid, + GFP_KERNEL); ieee80211_rx_bss_remove(sdata, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); @@ -1517,12 +1522,14 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, case WLAN_AUTH_LEAP: case WLAN_AUTH_FT: ieee80211_auth_completed(sdata); - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, + GFP_KERNEL); break; case WLAN_AUTH_SHARED_KEY: if (ifmgd->auth_transaction == 4) { ieee80211_auth_completed(sdata); - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, + GFP_KERNEL); } else ieee80211_auth_challenge(sdata, mgmt, len); break; @@ -1560,7 +1567,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_deauth(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); } @@ -1591,7 +1598,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, } ieee80211_set_disassoc(sdata, false, false, reason_code); - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); } @@ -1660,7 +1667,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, * association next time. This works around some broken APs * which do not correctly reject reassociation requests. */ ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, + GFP_KERNEL); if (ifmgd->flags & IEEE80211_STA_EXT_SME) { /* Wait for SME to decide what to do next */ ifmgd->state = IEEE80211_STA_MLME_DISABLED; @@ -1823,7 +1831,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ifmgd->last_beacon = jiffies; ieee80211_associated(sdata); - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0563b69..ec5acc6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1863,7 +1863,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev, !ieee80211_is_auth(hdr->frame_control)) goto ignore; - mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL); + mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL, + GFP_ATOMIC); ignore: dev_kfree_skb(rx->skb); rx->skb = NULL; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index dcfae88..7077869 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -122,7 +122,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, - (void *) skb->data, NULL); + (void *) skb->data, NULL, + GFP_ATOMIC); return RX_DROP_UNUSABLE; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index e56bbea..c4e6d4b 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -12,35 +12,35 @@ #include "core.h" #include "nl80211.h" -void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_rx_auth(rdev, dev, buf, len); + nl80211_send_rx_auth(rdev, dev, buf, len, gfp); } EXPORT_SYMBOL(cfg80211_send_rx_auth); -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_rx_assoc(rdev, dev, buf, len); + nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_deauth(rdev, dev, buf, len); + nl80211_send_deauth(rdev, dev, buf, len, gfp); } EXPORT_SYMBOL(cfg80211_send_deauth); -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_disassoc(rdev, dev, buf, len); + nl80211_send_disassoc(rdev, dev, buf, len, gfp); } EXPORT_SYMBOL(cfg80211_send_disassoc); @@ -53,33 +53,33 @@ static void cfg80211_wext_disconnected(struct net_device *dev) #endif } -void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) +void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_auth_timeout(rdev, dev, addr); + nl80211_send_auth_timeout(rdev, dev, addr, gfp); cfg80211_wext_disconnected(dev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); -void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) +void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_assoc_timeout(rdev, dev, addr); + nl80211_send_assoc_timeout(rdev, dev, addr, gfp); cfg80211_wext_disconnected(dev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, enum nl80211_key_type key_type, int key_id, - const u8 *tsc) + const u8 *tsc, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; - char *buf = kmalloc(128, GFP_ATOMIC); + char *buf = kmalloc(128, gfp); if (buf) { sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" @@ -93,6 +93,6 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, } #endif - nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc); + nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7946b82..01523ba 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3832,12 +3832,12 @@ nla_put_failure: 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) + enum nl80211_commands cmd, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -3856,7 +3856,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -3865,42 +3865,45 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, } void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, size_t len) + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE); + NL80211_CMD_AUTHENTICATE, gfp); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len) + size_t len, gfp_t gfp) { - nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE); + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_ASSOCIATE, gfp); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, size_t len) + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE); + NL80211_CMD_DEAUTHENTICATE, gfp); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len) + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE); + NL80211_CMD_DISASSOCIATE, gfp); } static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, int cmd, - const u8 *addr) + const u8 *addr, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -3920,7 +3923,7 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -3929,16 +3932,19 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, } void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr) + struct net_device *netdev, const u8 *addr, + gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, - addr); + addr, gfp); } void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr) + struct net_device *netdev, const u8 *addr, + gfp_t gfp) { - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr); + nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, + addr, gfp); } void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, @@ -3978,12 +3984,12 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, - const u8 *tsc) + const u8 *tsc, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -4007,7 +4013,7 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index cf0d271..662c216 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -15,27 +15,27 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, void nl80211_send_reg_change_event(struct regulatory_request *request); void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); + const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); + const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_deauth(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); + const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); + const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *addr); + const u8 *addr, gfp_t gfp); void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *addr); + const u8 *addr, gfp_t gfp); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, - int key_id, const u8 *tsc); + int key_id, const u8 *tsc, gfp_t gfp); void nl80211_send_beacon_hint_event(struct wiphy *wiphy, -- cgit v1.1 From 7ebbe6bd51a259e16608b3fd7b578f5dd1292a45 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:48 +0200 Subject: cfg80211: remove wireless_dev->bssid This variable isn't necessary -- the wext code keeps track of the BSSID itself, and otherwise we have current_bss. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/ibss.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a4a1c34..34b11ea 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -24,9 +24,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) if (WARN_ON(!wdev->ssid_len)) return; - if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0) - return; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); @@ -41,7 +38,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) cfg80211_hold_bss(bss); wdev->current_bss = bss; - memcpy(wdev->bssid, bssid, ETH_ALEN); nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); #ifdef CONFIG_WIRELESS_EXT @@ -87,7 +83,6 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) wdev->current_bss = NULL; wdev->ssid_len = 0; - memset(wdev->bssid, 0, ETH_ALEN); #ifdef CONFIG_WIRELESS_EXT if (!nowext) wdev->wext.ibss.ssid_len = 0; @@ -356,12 +351,10 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; - if (wdev->wext.ibss.bssid) { + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); - return 0; - } - - memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN); return 0; } /* temporary symbol - mark GPL - in the future the handler won't be */ -- cgit v1.1 From 0575606b086ad216b7b1976ca9b9a6e711db92f0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:49 +0200 Subject: mac80211: tell SME about real auth state When the auth algorithm is rejected, but we don't have another one to try, we will eventually retry but that isn't useful -- we'll then do it again and again until we eventually give up. Instead, we should let the SME know and go into disabled state. The same applies for situations where the AP rejects with any other status code. Additionally, when trying the next auth algorithm, we should reset the auth_tries so that just a single lost frame doesn't lead to us giving up on the third auth algorithm. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 383392b..58135a5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1511,9 +1511,15 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, !ieee80211_sta_wep_configured(sdata)) continue; ifmgd->auth_alg = algs[pos]; - break; + ifmgd->auth_tries = 0; + return; } } + /* nothing else to try -- give up */ + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, + GFP_KERNEL); + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + ieee80211_recalc_idle(sdata->local); return; } -- cgit v1.1 From 5121ea0481f9cea1dfd958f18d7b4ac78778cd40 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:50 +0200 Subject: wext: constify extra argument to wireless_send_event This is never changed by the function, so can be marked const. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 5da07e0..425f7d5 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1376,7 +1376,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) void wireless_send_event(struct net_device * dev, unsigned int cmd, union iwreq_data * wrqu, - char * extra) + const char * extra) { const struct iw_ioctl_description * descr = NULL; int extra_len = 0; -- cgit v1.1 From aff89a9b9084931e51b89d8f3ee3c547bea6c422 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:51 +0200 Subject: cfg80211: introduce nl80211 testmode command This introduces a new NL80211_CMD_TESTMODE for testing and calibration use with nl80211. There's no multiplexing like like iwpriv had, and the command is not available by default, it needs to be explicitly enabled in Kconfig and shouldn't be enabled in most kernels. The command requires a wiphy index or interface index to identify the device to operate on, and the new TESTDATA attribute. There also is API for sending replies to the command, and testmode multicast messages (on a testmode multicast group). I've also updated mac80211 to be able to pass through the command to the driver, since it itself doesn't implement the testmode command. Additionally, to give people an idea of how to use the command, I've added a little code to hwsim that makes use of the new command to set the powersave mode, this is currently done via debugfs and should remain there, and the testmode command only serves as an example of how to use this best -- with nested netlink attributes in the TESTDATA attribute. A hwsim testmode tool can be found at http://git.sipsolutions.net/hwsim.git/. This tool is BSD licensed so people can easily use it as a basis for their own internal fabrication and validation tools. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 13 +++++ net/wireless/Kconfig | 15 ++++++ net/wireless/core.h | 4 ++ net/wireless/nl80211.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index eb93eb6..c34c1a4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1376,6 +1376,18 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy) drv_rfkill_poll(local); } +#ifdef CONFIG_NL80211_TESTMODE +int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->testmode_cmd) + return -EOPNOTSUPP; + + return local->ops->testmode_cmd(&local->hw, data, len); +} +#endif + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1418,4 +1430,5 @@ struct cfg80211_ops mac80211_config_ops = { .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, .rfkill_poll = ieee80211_rfkill_poll, + CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) }; diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index ec64571..0402631 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -2,6 +2,21 @@ config CFG80211 tristate "Improved wireless configuration API" depends on RFKILL || !RFKILL +config NL80211_TESTMODE + bool "nl80211 testmode command" + depends on CFG80211 + help + The nl80211 testmode command helps implementing things like + factory calibration or validation tools for wireless chips. + + Select this option ONLY for kernels that are specifically + built for such purposes. + + Debugging tools that are supposed to end up in the hands of + users should better be implemented with debugfs. + + Say N. + config CFG80211_REG_DEBUG bool "cfg80211 regulatory debugging" depends on CFG80211 diff --git a/net/wireless/core.h b/net/wireless/core.h index bfa340c..bc084b6 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -58,6 +58,10 @@ struct cfg80211_registered_device { struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; +#ifdef CONFIG_NL80211_TESTMODE + struct genl_info *testmode_info; +#endif + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 01523ba..bb8de26 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3416,6 +3416,128 @@ unlock_rtnl: return err; } +#ifdef CONFIG_NL80211_TESTMODE +static struct genl_multicast_group nl80211_testmode_mcgrp = { + .name = "testmode", +}; + +static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int err; + + if (!info->attrs[NL80211_ATTR_TESTDATA]) + return -EINVAL; + + rtnl_lock(); + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + goto unlock_rtnl; + } + + err = -EOPNOTSUPP; + if (rdev->ops->testmode_cmd) { + rdev->testmode_info = info; + err = rdev->ops->testmode_cmd(&rdev->wiphy, + nla_data(info->attrs[NL80211_ATTR_TESTDATA]), + nla_len(info->attrs[NL80211_ATTR_TESTDATA])); + rdev->testmode_info = NULL; + } + + cfg80211_put_dev(rdev); + + unlock_rtnl: + rtnl_unlock(); + return err; +} + +static struct sk_buff * +__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, + int approxlen, u32 pid, u32 seq, gfp_t gfp) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *data; + + skb = nlmsg_new(approxlen + 100, gfp); + if (!skb) + return NULL; + + hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); + if (!hdr) { + kfree_skb(skb); + return NULL; + } + + NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + + ((void **)skb->cb)[0] = rdev; + ((void **)skb->cb)[1] = hdr; + ((void **)skb->cb)[2] = data; + + return skb; + + nla_put_failure: + kfree_skb(skb); + return NULL; +} + +struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, + int approxlen) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!rdev->testmode_info)) + return NULL; + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, + rdev->testmode_info->snd_pid, + rdev->testmode_info->snd_seq, + GFP_KERNEL); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); + +int cfg80211_testmode_reply(struct sk_buff *skb) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + if (WARN_ON(!rdev->testmode_info)) { + kfree_skb(skb); + return -EINVAL; + } + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, rdev->testmode_info); +} +EXPORT_SYMBOL(cfg80211_testmode_reply); + +struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, + int approxlen, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); + +void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) +{ + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_event); +#endif + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -3629,6 +3751,14 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, +#ifdef CONFIG_NL80211_TESTMODE + { + .cmd = NL80211_CMD_TESTMODE, + .doit = nl80211_testmode_do, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +#endif }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4102,6 +4232,12 @@ int nl80211_init(void) if (err) goto err_out; +#ifdef CONFIG_NL80211_TESTMODE + err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); + if (err) + goto err_out; +#endif + return 0; err_out: genl_unregister_family(&nl80211_fam); -- cgit v1.1 From 3f65b24536996ac493777ef11474c47e1d3f56a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:52 +0200 Subject: mac80211: remove an unused function declaration The ieee80211_scan_results function hasn't existed for a long time now, so its declaration should be removed as well. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e0323e5..1950e2e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -981,9 +981,6 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); -int ieee80211_scan_results(struct ieee80211_local *local, - struct iw_request_info *info, - char *buf, size_t len); void ieee80211_scan_cancel(struct ieee80211_local *local); ieee80211_rx_result ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -- cgit v1.1 From b23aa676ab9d54469cda9f7151f51a2851c6f36e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 1 Jul 2009 21:26:54 +0200 Subject: cfg80211: connect/disconnect API This patch introduces the cfg80211 connect/disconnect API. The goal here is to run the AUTH and ASSOC steps in one call. This is needed for some fullmac cards that run both steps directly from the target, after the host driver sends a connect command. Additionally, all the new crypto parameters for connect() are now also valid for associate() -- although associate requires the IEs to be used, the information can be useful for drivers and should be given. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 2 +- net/wireless/Makefile | 2 +- net/wireless/core.c | 16 ++- net/wireless/core.h | 7 + net/wireless/nl80211.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++++- net/wireless/nl80211.h | 13 ++ net/wireless/sme.c | 224 ++++++++++++++++++++++++++++++ 7 files changed, 620 insertions(+), 12 deletions(-) create mode 100644 net/wireless/sme.c (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c34c1a4..03de402 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1262,7 +1262,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; } - if (req->control_port) + if (req->crypto.control_port) sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; else sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f78c483..750c08e3 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 mlme.o ibss.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o diff --git a/net/wireless/core.c b/net/wireless/core.c index d41b741..314e00f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -546,6 +546,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, "symlink to netdev!\n"); } wdev->netdev = dev; + wdev->sme_state = CFG80211_SME_IDLE; #ifdef CONFIG_WIRELESS_EXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; @@ -553,11 +554,20 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_GOING_DOWN: - if (wdev->iftype != NL80211_IFTYPE_ADHOC) - break; if (!wdev->ssid_len) break; - cfg80211_leave_ibss(rdev, dev, true); + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, true); + break; + case NL80211_IFTYPE_STATION: + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING); + break; + default: + break; + } break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT diff --git a/net/wireless/core.h b/net/wireless/core.h index bc084b6..f93f96f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -174,6 +174,13 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +/* SME */ +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason); + /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bb8de26..89dd379 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -128,6 +128,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = sizeof(struct nl80211_sta_flag_update), }, [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, + [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, + [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, }; /* IE validation */ @@ -347,6 +350,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(join_ibss, JOIN_IBSS); #undef CMD + + if (dev->ops->connect) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); + } + + if (dev->ops->disconnect) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); + } + nla_nest_end(msg, nl_cmds); return genlmsg_end(msg, hdr); @@ -3001,12 +3015,31 @@ static int nl80211_dump_scan(struct sk_buff *skb, static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) { - return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || - auth_type == NL80211_AUTHTYPE_SHARED_KEY || - auth_type == NL80211_AUTHTYPE_FT || - auth_type == NL80211_AUTHTYPE_NETWORK_EAP; + return auth_type <= NL80211_AUTHTYPE_MAX; +} + +static bool nl80211_valid_wpa_versions(u32 wpa_versions) +{ + return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)); +} + +static bool nl80211_valid_akm_suite(u32 akm) +{ + return akm == WLAN_AKM_SUITE_8021X || + akm == WLAN_AKM_SUITE_PSK; +} + +static bool nl80211_valid_cipher_suite(u32 cipher) +{ + return cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104 || + cipher == WLAN_CIPHER_SUITE_TKIP || + cipher == WLAN_CIPHER_SUITE_CCMP || + cipher == WLAN_CIPHER_SUITE_AES_CMAC; } + static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3086,6 +3119,68 @@ unlock_rtnl: return err; } +static int nl80211_crypto_settings(struct genl_info *info, + struct cfg80211_crypto_settings *settings) +{ + settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; + + if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + settings->n_ciphers_pairwise = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + if (settings->n_ciphers_pairwise > NL80211_MAX_NR_CIPHER_SUITES) + return -EINVAL; + + memcpy(settings->ciphers_pairwise, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_cipher_suite( + settings->ciphers_pairwise[i])) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { + settings->cipher_group = + nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); + if (!nl80211_valid_cipher_suite(settings->cipher_group)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { + settings->wpa_versions = + nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); + if (!nl80211_valid_wpa_versions(settings->wpa_versions)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_AKM_SUITES]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); + len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); + settings->n_akm_suites = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + memcpy(settings->akm_suites, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_akm_suite(settings->akm_suites[i])) + return -EINVAL; + } + + return 0; +} + static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *drv; @@ -3156,9 +3251,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } - req.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; - - err = drv->ops->assoc(&drv->wiphy, dev, &req); + err = nl80211_crypto_settings(info, &req.crypto); + if (!err) + err = drv->ops->assoc(&drv->wiphy, dev, &req); out: cfg80211_put_dev(drv); @@ -3538,6 +3633,130 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) EXPORT_SYMBOL(cfg80211_testmode_event); #endif +static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_connect_params connect; + struct wiphy *wiphy; + int err; + + memset(&connect, 0, sizeof(connect)); + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SSID] || + !nla_len(info->attrs[NL80211_ATTR_SSID])) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + connect.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(connect.auth_type)) + return -EINVAL; + } else + connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; + + err = nl80211_crypto_settings(info, &connect.crypto); + if (err) + return err; + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + wiphy = &drv->wiphy; + + connect.bssid = NULL; + connect.channel = NULL; + connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + + if (info->attrs[NL80211_ATTR_MAC]) + connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + connect.channel = + ieee80211_get_channel(wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!connect.channel || + connect.channel->flags & IEEE80211_CHAN_DISABLED) { + err = -EINVAL; + goto out; + } + } + + err = cfg80211_connect(drv, dev, &connect); + +out: + cfg80211_put_dev(drv); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + int err; + u16 reason; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + reason = WLAN_REASON_DEAUTH_LEAVING; + else + reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (reason == 0) + return -EINVAL; + + rtnl_lock(); + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + err = cfg80211_disconnect(drv, dev, reason); + +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, @@ -3759,6 +3978,18 @@ static struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, }, #endif + { + .cmd = NL80211_CMD_CONNECT, + .doit = nl80211_connect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISCONNECT, + .doit = nl80211_disconnect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4077,6 +4308,129 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, addr, gfp); } +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); + 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); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); + 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_MAC, ETH_ALEN, bssid); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); + 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); + if (from_ap && reason) + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); + if (from_ap) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); + if (ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, gfp_t gfp) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 662c216..cf3708b 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -31,6 +31,19 @@ void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp); + void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 0000000..fc11703 --- /dev/null +++ b/net/wireless/sme.c @@ -0,0 +1,224 @@ +/* + * SME code for cfg80211's connect emulation. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "nl80211.h" + + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + wdev->current_bss = NULL; + } + + if (status == WLAN_STATUS_SUCCESS) { + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss); + wdev->current_bss = bss; + + wdev->sme_state = CFG80211_SME_CONNECTED; + } else { + wdev->sme_state = CFG80211_SME_IDLE; + } + + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + status, gfp); + +#ifdef CONFIG_WIRELESS_EXT + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} +EXPORT_SYMBOL(cfg80211_connect_result); + +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + /* internal error -- how did we get to CONNECTED w/o BSS? */ + if (WARN_ON(!wdev->current_bss)) { + return; + } + + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + wdev->current_bss = NULL; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss); + wdev->current_bss = bss; + + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); + +#ifdef CONFIG_WIRELESS_EXT + if (req_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} +EXPORT_SYMBOL(cfg80211_roamed); + +static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, + u8 *ie, size_t ie_len, u16 reason, + bool from_ap) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(wdev->current_bss); + } + + wdev->current_bss = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + + nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, + reason, ie, ie_len, from_ap, gfp); + +#ifdef CONFIG_WIRELESS_EXT + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} + +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp) +{ + __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); +} +EXPORT_SYMBOL(cfg80211_disconnected); + +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + int err; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->sme_state != CFG80211_SME_IDLE) + return -EALREADY; + + if (!rdev->ops->connect) { + return -EOPNOTSUPP; + } else { + wdev->sme_state = CFG80211_SME_CONNECTING; + err = rdev->ops->connect(&rdev->wiphy, dev, connect); + if (err) { + wdev->sme_state = CFG80211_SME_IDLE; + return err; + } + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + + return 0; +} + +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason) +{ + int err; + + if (!rdev->ops->disconnect) { + return -EOPNOTSUPP; + } else { + err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); + if (err) + return err; + } + + __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); + + return 0; +} -- cgit v1.1 From 6829c878ecd24ff0ae41b4668c7e9d0f11b66942 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 09:13:27 +0200 Subject: cfg80211: emulate connect with auth/assoc This adds code to cfg80211 so that drivers (mac80211 right now) that don't implement connect but rather auth/assoc can still be used with the nl80211 connect command. This will also be necessary for the wext compat code. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 7 + net/wireless/core.h | 8 + net/wireless/mlme.c | 79 ++++++++-- net/wireless/nl80211.c | 4 +- net/wireless/scan.c | 7 + net/wireless/sme.c | 395 +++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 469 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 314e00f..a0a6797 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -321,6 +321,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) } INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&drv->conn_work, cfg80211_conn_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. @@ -481,6 +482,8 @@ void wiphy_unregister(struct wiphy *wiphy) /* unlock again before freeing */ mutex_unlock(&drv->mtx); + cancel_work_sync(&drv->conn_work); + cfg80211_debugfs_drv_del(drv); /* If this device got a regulatory hint tell core its @@ -569,6 +572,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } break; + case NETDEV_DOWN: + kfree(wdev->conn); + wdev->conn = NULL; + break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT if (wdev->iftype != NL80211_IFTYPE_ADHOC) diff --git a/net/wireless/core.h b/net/wireless/core.h index f93f96f..2c0f642 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -62,6 +62,8 @@ struct cfg80211_registered_device { struct genl_info *testmode_info; #endif + struct work_struct conn_work; + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { @@ -181,8 +183,14 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason); +void cfg80211_conn_work(struct work_struct *work); + /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); +void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, + size_t ie_len, u16 reason, bool from_ap); +void cfg80211_sme_scan_done(struct net_device *dev); +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c4e6d4b..3427fe7 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -16,58 +16,105 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gf { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + nl80211_send_rx_auth(rdev, dev, buf, len, gfp); + cfg80211_sme_rx_auth(dev, buf, len); } EXPORT_SYMBOL(cfg80211_send_rx_auth); void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + u16 status_code; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *ie = mgmt->u.assoc_resp.variable; + int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); + + cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, gfp); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + nl80211_send_deauth(rdev, dev, buf, len, gfp); + + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); + + wdev->sme_state = CFG80211_SME_IDLE; + } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { + cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + } } EXPORT_SYMBOL(cfg80211_send_deauth); void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + nl80211_send_disassoc(rdev, dev, buf, len, gfp); -} -EXPORT_SYMBOL(cfg80211_send_disassoc); -static void cfg80211_wext_disconnected(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - union iwreq_data wrqu; - memset(&wrqu, 0, sizeof(wrqu)); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -#endif + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); + + wdev->sme_state = CFG80211_SME_IDLE; + } } +EXPORT_SYMBOL(cfg80211_send_disassoc); void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_auth_timeout(rdev, dev, addr, gfp); - cfg80211_wext_disconnected(dev); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + wdev->sme_state = CFG80211_SME_IDLE; } EXPORT_SYMBOL(cfg80211_send_auth_timeout); void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); nl80211_send_assoc_timeout(rdev, dev, addr, gfp); - cfg80211_wext_disconnected(dev); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + wdev->sme_state = CFG80211_SME_IDLE; } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89dd379..89aa9e7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -351,12 +351,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, #undef CMD - if (dev->ops->connect) { + if (dev->ops->connect || dev->ops->auth) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); } - if (dev->ops->disconnect) { + if (dev->ops->disconnect || dev->ops->deauth) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 261a063..82b33e7 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -30,6 +30,13 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + /* + * This must be before sending the other events! + * Otherwise, wpa_supplicant gets completely confused with + * wext events. + */ + cfg80211_sme_scan_done(dev); + if (aborted) nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); else diff --git a/net/wireless/sme.c b/net/wireless/sme.c index fc11703..3abb047 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -12,6 +12,266 @@ #include #include "nl80211.h" +struct cfg80211_conn { + struct cfg80211_connect_params params; + /* these are sub-states of the _CONNECTING sme_state */ + enum { + CFG80211_CONN_IDLE, + CFG80211_CONN_SCANNING, + CFG80211_CONN_SCAN_AGAIN, + CFG80211_CONN_AUTHENTICATE_NEXT, + CFG80211_CONN_AUTHENTICATING, + CFG80211_CONN_ASSOCIATE_NEXT, + CFG80211_CONN_ASSOCIATING, + } state; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ie_len; + bool auto_auth; +}; + + +static int cfg80211_conn_scan(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_scan_request *request; + int n_channels, err; + + ASSERT_RTNL(); + + if (drv->scan_req) + return -EBUSY; + + if (wdev->conn->params.channel) { + n_channels = 1; + } else { + enum ieee80211_band band; + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + n_channels += wdev->wiphy->bands[band]->n_channels; + } + } + request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + + sizeof(request->channels[0]) * n_channels, + GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->channels = (void *)((char *)request + sizeof(*request)); + if (wdev->conn->params.channel) + request->channels[0] = wdev->conn->params.channel; + else { + int i = 0, j; + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + for (j = 0; j < wdev->wiphy->bands[band]->n_channels; + i++, j++) + request->channels[i] = + &wdev->wiphy->bands[band]->channels[j]; + } + } + request->n_channels = n_channels; + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = 1; + + memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, + wdev->conn->params.ssid_len); + request->ssids[0].ssid_len = wdev->conn->params.ssid_len; + + request->ifidx = wdev->netdev->ifindex; + request->wiphy = &drv->wiphy; + + drv->scan_req = request; + + err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); + if (!err) { + wdev->conn->state = CFG80211_CONN_SCANNING; + nl80211_send_scan_start(drv, wdev->netdev); + } else { + drv->scan_req = NULL; + kfree(request); + } + return err; +} + +static int cfg80211_conn_do_work(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + union { + struct cfg80211_auth_request auth_req; + struct cfg80211_assoc_request assoc_req; + } u; + + memset(&u, 0, sizeof(u)); + + if (!wdev->conn) + return 0; + + switch (wdev->conn->state) { + case CFG80211_CONN_SCAN_AGAIN: + return cfg80211_conn_scan(wdev); + case CFG80211_CONN_AUTHENTICATE_NEXT: + u.auth_req.chan = wdev->conn->params.channel; + u.auth_req.peer_addr = wdev->conn->params.bssid; + u.auth_req.ssid = wdev->conn->params.ssid; + u.auth_req.ssid_len = wdev->conn->params.ssid_len; + u.auth_req.auth_type = wdev->conn->params.auth_type; + u.auth_req.ie = NULL; + u.auth_req.ie_len = 0; + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + BUG_ON(!drv->ops->auth); + return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); + case CFG80211_CONN_ASSOCIATE_NEXT: + u.assoc_req.chan = wdev->conn->params.channel; + u.assoc_req.peer_addr = wdev->conn->params.bssid; + u.assoc_req.ssid = wdev->conn->params.ssid; + u.assoc_req.ssid_len = wdev->conn->params.ssid_len; + u.assoc_req.ie = wdev->conn->params.ie; + u.assoc_req.ie_len = wdev->conn->params.ie_len; + u.assoc_req.use_mfp = false; + memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, + sizeof(u.assoc_req.crypto)); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + BUG_ON(!drv->ops->assoc); + return drv->ops->assoc(wdev->wiphy, wdev->netdev, + &u.assoc_req); + default: + return 0; + } +} + +void cfg80211_conn_work(struct work_struct *work) +{ + struct cfg80211_registered_device *drv = + container_of(work, struct cfg80211_registered_device, conn_work); + struct wireless_dev *wdev; + + rtnl_lock(); + mutex_lock(&drv->devlist_mtx); + + list_for_each_entry(wdev, &drv->netdev_list, list) { + if (!netif_running(wdev->netdev)) + continue; + if (wdev->sme_state != CFG80211_SME_CONNECTING) + continue; + if (cfg80211_conn_do_work(wdev)) + cfg80211_connect_result(wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + } + + mutex_unlock(&drv->devlist_mtx); + rtnl_unlock(); +} + +static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bss *bss; + u16 capa = WLAN_CAPABILITY_ESS; + + if (wdev->conn->params.privacy) + capa |= WLAN_CAPABILITY_PRIVACY; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, + wdev->conn->params.ssid, + wdev->conn->params.ssid_len, + WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, + capa); + + if (!bss) + return false; + + memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); + wdev->conn->params.bssid = wdev->conn->bssid; + wdev->conn->params.channel = bss->channel; + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&drv->conn_work); + + cfg80211_put_bss(bss); + return true; +} + +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (wdev->conn->state != CFG80211_CONN_SCANNING && + wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) + return; + + if (!cfg80211_get_conn_bss(wdev)) { + /* not found */ + if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) + schedule_work(&drv->conn_work); + else + cfg80211_connect_result(dev, wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + return; + } +} + +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + + /* should only RX auth frames when connecting */ + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + wdev->conn->auto_auth && + wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { + /* select automatically between only open, shared, leap */ + switch (wdev->conn->params.auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; + break; + default: + /* huh? */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + } + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + } else if (status_code != WLAN_STATUS_SUCCESS) + wdev->sme_state = CFG80211_SME_IDLE; + else if (wdev->sme_state == CFG80211_SME_CONNECTING && + wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + } +} void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, @@ -27,7 +287,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; if (wdev->current_bss) { @@ -53,6 +313,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->sme_state = CFG80211_SME_IDLE; } + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, gfp); @@ -72,7 +335,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid) + if (bssid && status == WLAN_STATUS_SUCCESS) memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif @@ -138,9 +401,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, } EXPORT_SYMBOL(cfg80211_roamed); -static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, - u8 *ie, size_t ie_len, u16 reason, - bool from_ap) +void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, + size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; #ifdef CONFIG_WIRELESS_EXT @@ -161,6 +423,11 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; + if (wdev->conn) { + kfree(wdev->conn->ie); + wdev->conn->ie = NULL; + } + nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, reason, ie, ie_len, from_ap, gfp); @@ -174,7 +441,7 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { - __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); + __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); } EXPORT_SYMBOL(cfg80211_disconnected); @@ -189,7 +456,74 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, return -EALREADY; if (!rdev->ops->connect) { - return -EOPNOTSUPP; + if (!rdev->ops->auth || !rdev->ops->assoc) + return -EOPNOTSUPP; + + if (!wdev->conn) { + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; + } else + memset(wdev->conn, 0, sizeof(*wdev->conn)); + + /* + * Copy all parameters, and treat explicitly IEs, BSSID, SSID. + */ + memcpy(&wdev->conn->params, connect, sizeof(*connect)); + if (connect->bssid) { + wdev->conn->params.bssid = wdev->conn->bssid; + memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); + } + + if (connect->ie) { + wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, + GFP_KERNEL); + wdev->conn->params.ie = wdev->conn->ie; + if (!wdev->conn->ie) + return -ENOMEM; + } + + if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + wdev->conn->auto_auth = true; + /* start with open system ... should mostly work */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } else { + wdev->conn->auto_auth = false; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + wdev->conn->params.ssid = wdev->ssid; + wdev->conn->params.ssid_len = connect->ssid_len; + + /* don't care about result -- but fill bssid & channel */ + if (!wdev->conn->params.bssid || !wdev->conn->params.channel) + cfg80211_get_conn_bss(wdev); + + wdev->sme_state = CFG80211_SME_CONNECTING; + + /* we're good if we have both BSSID and channel */ + if (wdev->conn->params.bssid && wdev->conn->params.channel) { + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + err = cfg80211_conn_do_work(wdev); + } else { + /* otherwise we'll need to scan for the AP first */ + err = cfg80211_conn_scan(wdev); + /* + * If we can't scan right now, then we need to scan again + * after the current scan finished, since the parameters + * changed (unless we find a good AP anyway). + */ + if (err == -EBUSY) { + err = 0; + wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; + } + } + if (err) + wdev->sme_state = CFG80211_SME_IDLE; + + return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; err = rdev->ops->connect(&rdev->wiphy, dev, connect); @@ -197,28 +531,63 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_IDLE; return err; } - } - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; - return 0; + return 0; + } } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason) { + struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + if (wdev->sme_state == CFG80211_SME_IDLE) + return -EINVAL; + if (!rdev->ops->disconnect) { - return -EOPNOTSUPP; + struct cfg80211_deauth_request deauth; + u8 bssid[ETH_ALEN]; + + /* internal bug. */ + if (WARN_ON(!wdev->conn)) + return -EINVAL; + + if (wdev->sme_state == CFG80211_SME_CONNECTING && + (wdev->conn->state == CFG80211_CONN_SCANNING || + wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { + wdev->sme_state = CFG80211_SME_IDLE; + return 0; + } + + if (!rdev->ops->deauth) + return -EOPNOTSUPP; + + memset(&deauth, 0, sizeof(deauth)); + + /* wdev->conn->params.bssid must be set if > SCANNING */ + memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); + deauth.peer_addr = bssid; + deauth.reason_code = reason; + + err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); + if (err) + return err; } else { err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); if (err) return err; } - __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTED) + __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); + else if (wdev->sme_state == CFG80211_SME_CONNECTING) + cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); return 0; } -- cgit v1.1 From f21293549f60f88c74fcb9944737f11048896dc4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:56 +0200 Subject: cfg80211: managed mode wext compatibility This adds code to make it possible to use the cfg80211 connect() API with wireless extensions, and because the previous patch added emulation of that API with auth() and assoc(), by extension also supports wext on that. At the same time, removes code from mac80211 for wext, but doesn't yet clean up mac80211's mlme code more. Signed-off-by: Samuel Ortiz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 69 ---------- net/mac80211/wext.c | 215 +++-------------------------- net/wireless/Makefile | 2 +- net/wireless/core.c | 23 +++- net/wireless/core.h | 3 +- net/wireless/nl80211.c | 2 +- net/wireless/sme.c | 55 +++++--- net/wireless/wext-compat.c | 229 +++++++++++++++++++++++++------ net/wireless/wext-sme.c | 329 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 590 insertions(+), 337 deletions(-) create mode 100644 net/wireless/wext-sme.c (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 58135a5..fbb93a7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -870,70 +870,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, return changed; } -static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata) -{ - union iwreq_data wrqu; - - memset(&wrqu, 0, sizeof(wrqu)); - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) - memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL); -} - -static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - char *buf; - size_t len; - int i; - union iwreq_data wrqu; - - if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies) - return; - - buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len + - ifmgd->assocresp_ies_len), GFP_KERNEL); - if (!buf) - return; - - len = sprintf(buf, "ASSOCINFO("); - if (ifmgd->assocreq_ies) { - len += sprintf(buf + len, "ReqIEs="); - for (i = 0; i < ifmgd->assocreq_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocreq_ies[i]); - } - } - if (ifmgd->assocresp_ies) { - if (ifmgd->assocreq_ies) - len += sprintf(buf + len, " "); - len += sprintf(buf + len, "RespIEs="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - len += sprintf(buf + len, ")"); - - if (len > IW_CUSTOM_MAX) { - len = sprintf(buf, "ASSOCRESPIE="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - - if (len <= IW_CUSTOM_MAX) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = len; - wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); - } - - kfree(buf); -} - - static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, u32 bss_info_changed) { @@ -966,7 +902,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET; memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN); - ieee80211_sta_send_associnfo(sdata); ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); @@ -993,8 +928,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); - - ieee80211_sta_send_apinfo(sdata); } static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) @@ -1147,8 +1080,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; - ieee80211_sta_send_apinfo(sdata); - if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) { ifmgd->state = IEEE80211_STA_MLME_DISABLED; ieee80211_rx_bss_remove(sdata, ifmgd->bssid, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 1da81f4..d4e61dc 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,29 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); - if (ret && ret != -EALREADY) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - if (ret != -EALREADY) - ieee80211_sta_req_auth(sdata); - return 0; - } - - return -EOPNOTSUPP; -} - static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) @@ -61,16 +38,13 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); else if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; + return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ if (freq->e == 0) { - if (freq->m < 0) { - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags |= - IEEE80211_STA_AUTO_CHANNEL_SEL; - return 0; - } else + if (freq->m < 0) + return -EINVAL; + else chan = ieee80211_get_channel(local->hw.wiphy, ieee80211_channel_to_frequency(freq->m)); } else { @@ -95,9 +69,6 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (local->oper_channel == chan) return 0; - if (sdata->vif.type == NL80211_IFTYPE_STATION) - ieee80211_sta_req_auth(sdata); - local->oper_channel = chan; local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, 0); @@ -115,6 +86,8 @@ static int ieee80211_ioctl_giwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); freq->m = local->oper_channel->center_freq; freq->e = 6; @@ -128,31 +101,11 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - size_t len = data->length; - int ret; if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->flags) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; - else - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; - - ret = ieee80211_sta_set_ssid(sdata, ssid, len); - if (ret) - return ret; - - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -162,23 +115,14 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { - size_t len; struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int res = ieee80211_sta_get_ssid(sdata, ssid, &len); - if (res == 0) { - data->length = len; - data->flags = 1; - } else - data->flags = 0; - return res; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -193,24 +137,10 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret; + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL; - else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); - if (ret) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { + if (sdata->vif.type == NL80211_IFTYPE_WDS) { /* * If it is necessary to update the WDS peer address * while the interface is running, then we need to do @@ -240,14 +170,10 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN); - } else - memset(&ap_addr->sa_data, 0, ETH_ALEN); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); + + if (sdata->vif.type == NL80211_IFTYPE_WDS) { ap_addr->sa_family = ARPHRD_ETHER; memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); return 0; @@ -395,85 +321,6 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev, return 0; } -static int ieee80211_ioctl_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_WPA_ENABLED: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_CIPHER_GROUP_MGMT: - break; - case IW_AUTH_CIPHER_PAIRWISE: - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->value & (IW_AUTH_CIPHER_WEP40 | - IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP)) - sdata->u.mgd.flags |= - IEEE80211_STA_TKIP_WEP_USED; - else - sdata->u.mgd.flags &= - ~IEEE80211_STA_TKIP_WEP_USED; - } - break; - case IW_AUTH_DROP_UNENCRYPTED: - sdata->drop_unencrypted = !!data->value; - break; - case IW_AUTH_PRIVACY_INVOKED: - if (sdata->vif.type != NL80211_IFTYPE_STATION) - ret = -EINVAL; - else { - sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; - /* - * Privacy invoked by wpa_supplicant, store the - * value and allow associating to a protected - * network without having a key up front. - */ - if (data->value) - sdata->u.mgd.flags |= - IEEE80211_STA_PRIVACY_INVOKED; - } - break; - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.auth_algs = data->value; - else - 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) { - switch (data->value) { - case IW_AUTH_MFP_DISABLED: - sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; - break; - case IW_AUTH_MFP_OPTIONAL: - sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL; - break; - case IW_AUTH_MFP_REQUIRED: - sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; - break; - default: - ret = -EINVAL; - } - } else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; -} - /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { @@ -541,28 +388,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev return wstats; } -static int ieee80211_ioctl_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - data->value = sdata->u.mgd.auth_algs; - else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; -} - - /* Structures to export the Wireless Handlers */ static const iw_handler ieee80211_handler[] = @@ -615,10 +440,10 @@ static const iw_handler ieee80211_handler[] = (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ - (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ + (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ (iw_handler) NULL, /* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 750c08e3..d74cc77 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -7,6 +7,6 @@ obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o +cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/core.c b/net/wireless/core.c index a0a6797..e2f80dd 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -553,6 +553,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #ifdef CONFIG_WIRELESS_EXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif mutex_unlock(&rdev->devlist_mtx); break; @@ -565,8 +566,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_STATION: +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.ie); + wdev->wext.ie = NULL; + wdev->wext.ie_len = 0; +#endif cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING); + WLAN_REASON_DEAUTH_LEAVING, true); break; default: break; @@ -578,11 +584,20 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT - if (wdev->iftype != NL80211_IFTYPE_ADHOC) + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + if (wdev->wext.ibss.ssid_len) + cfg80211_join_ibss(rdev, dev, + &wdev->wext.ibss); break; - if (!wdev->wext.ibss.ssid_len) + case NL80211_IFTYPE_STATION: + if (wdev->wext.connect.ssid_len) + cfg80211_connect(rdev, dev, + &wdev->wext.connect); + break; + default: break; - cfg80211_join_ibss(rdev, dev, &wdev->wext.ibss); + } #endif break; case NETDEV_UNREGISTER: diff --git a/net/wireless/core.h b/net/wireless/core.h index 2c0f642..5209acb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -181,7 +181,8 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason); + struct net_device *dev, u16 reason, + bool wextev); void cfg80211_conn_work(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89aa9e7..0008144 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3747,7 +3747,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_disconnect(drv, dev, reason); + err = cfg80211_disconnect(drv, dev, reason, true); out: cfg80211_put_dev(drv); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3abb047..f272ebf 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -273,10 +273,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } } -void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) +static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -321,25 +321,36 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, status, gfp); #ifdef CONFIG_WIRELESS_EXT - if (req_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); - } + if (wextev) { + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } - if (resp_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid && status == WLAN_STATUS_SUCCESS) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + bool wextev = status == WLAN_STATUS_SUCCESS; + __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp); +} EXPORT_SYMBOL(cfg80211_connect_result); void cfg80211_roamed(struct net_device *dev, const u8 *bssid, @@ -540,7 +551,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason) + struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -585,9 +596,9 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (wdev->sme_state == CFG80211_SME_CONNECTED) __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); else if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + wextev, GFP_KERNEL); return 0; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index cae3b52..02f052f 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -261,50 +261,6 @@ int cfg80211_wext_giwrange(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_mlme *mlme = (struct iw_mlme *)extra; - struct cfg80211_registered_device *rdev; - union { - struct cfg80211_disassoc_request disassoc; - struct cfg80211_deauth_request deauth; - } cmd; - - if (!wdev) - return -EOPNOTSUPP; - - rdev = wiphy_to_dev(wdev->wiphy); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (mlme->addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - cmd.deauth.peer_addr = mlme->addr.sa_data; - cmd.deauth.reason_code = mlme->reason_code; - return rdev->ops->deauth(wdev->wiphy, dev, &cmd.deauth); - case IW_MLME_DISASSOC: - if (!rdev->ops->disassoc) - return -EOPNOTSUPP; - cmd.disassoc.peer_addr = mlme->addr.sa_data; - cmd.disassoc.reason_code = mlme->reason_code; - return rdev->ops->disassoc(wdev->wiphy, dev, &cmd.disassoc); - default: - return -EOPNOTSUPP; - } -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); - /** * cfg80211_wext_freq - get wext frequency for non-"auto" @@ -846,3 +802,188 @@ int cfg80211_wext_giwtxpower(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwtxpower); + +static int cfg80211_set_auth_alg(struct wireless_dev *wdev, + s32 auth_alg) +{ + int nr_alg = 0; + + if (!auth_alg) + return -EINVAL; + + if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | + IW_AUTH_ALG_SHARED_KEY | + IW_AUTH_ALG_LEAP)) + return -EINVAL; + + if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; + } + + if (auth_alg & IW_AUTH_ALG_LEAP) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; + } + + if (nr_alg > 1) + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + return 0; +} + +static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) +{ + wdev->wext.connect.crypto.wpa_versions = 0; + + if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | + IW_AUTH_WPA_VERSION_WPA2)) + return -EINVAL; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_1; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_2; + + return 0; +} + +int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) +{ + wdev->wext.connect.crypto.cipher_group = 0; + + if (cipher & IW_AUTH_CIPHER_WEP40) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP40; + else if (cipher & IW_AUTH_CIPHER_WEP104) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP104; + else if (cipher & IW_AUTH_CIPHER_TKIP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_TKIP; + else if (cipher & IW_AUTH_CIPHER_CCMP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_CCMP; + else if (cipher & IW_AUTH_CIPHER_AES_CMAC) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_AES_CMAC; + else + return -EINVAL; + + return 0; +} + +int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) +{ + int nr_ciphers = 0; + u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; + + if (cipher & IW_AUTH_CIPHER_WEP40) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_WEP104) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_TKIP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_CCMP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_AES_CMAC) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; + nr_ciphers++; + } + + BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); + + wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; + + return 0; +} + + +int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) +{ + int nr_akm_suites = 0; + + if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | + IW_AUTH_KEY_MGMT_PSK)) + return -EINVAL; + + if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_8021X; + nr_akm_suites++; + } + + if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_PSK; + nr_akm_suites++; + } + + wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; + + return 0; +} + +int cfg80211_wext_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_PRIVACY_INVOKED: + wdev->wext.connect.privacy = data->value; + return 0; + case IW_AUTH_WPA_VERSION: + return cfg80211_set_wpa_version(wdev, data->value); + case IW_AUTH_CIPHER_GROUP: + return cfg80211_set_cipher_group(wdev, data->value); + case IW_AUTH_KEY_MGMT: + return cfg80211_set_key_mgt(wdev, data->value); + case IW_AUTH_CIPHER_PAIRWISE: + return cfg80211_set_cipher_pairwise(wdev, data->value); + case IW_AUTH_80211_AUTH_ALG: + return cfg80211_set_auth_alg(wdev, data->value); + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_MFP: + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwauth); + +int cfg80211_wext_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + /* XXX: what do we need? */ + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c new file mode 100644 index 0000000..3b531d5 --- /dev/null +++ b/net/wireless/wext-sme.c @@ -0,0 +1,329 @@ +/* + * cfg80211 wext compat for managed mode. + * + * Copyright 2009 Johannes Berg + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include "nl80211.h" + +static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int err; + + if (!netif_running(wdev->netdev)) + return 0; + + wdev->wext.connect.ie = wdev->wext.ie; + wdev->wext.connect.ie_len = wdev->wext.ie_len; + wdev->wext.connect.privacy = wdev->wext.default_key != -1; + + err = 0; + if (wdev->wext.connect.ssid_len != 0) + err = cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect); + + return err; +} + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + chan = cfg80211_wext_freq(wdev->wiphy, freq); + if (chan && IS_ERR(chan)) + return PTR_ERR(chan); + + if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + if (wdev->wext.connect.channel == chan) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set, we'll try right again, avoid event */ + if (wdev->wext.connect.ssid_len) + event = false; + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + return err; + } + + wdev->wext.connect.channel = chan; + + /* SSID is not set, we just want to switch channel */ + if (wdev->wext.connect.ssid_len && chan) { + if (!rdev->ops->set_channel) + return -EOPNOTSUPP; + + return rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + } + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); + +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan = NULL; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (wdev->current_bss) + chan = wdev->current_bss->channel; + else if (wdev->wext.connect.channel) + chan = wdev->wext.connect.channel; + + if (chan) { + freq->m = chan->center_freq; + freq->e = 6; + return 0; + } + + /* no channel if not joining */ + return -EINVAL; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq); + +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + size_t len = data->length; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (!data->flags) + len = 0; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + if (wdev->wext.connect.ssid && len && + len == wdev->wext.connect.ssid_len && + memcmp(wdev->wext.connect.ssid, ssid, len)) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set now, we'll try to connect, avoid event */ + if (len) + event = false; + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + return err; + } + + wdev->wext.connect.ssid = wdev->wext.ssid; + memcpy(wdev->wext.ssid, ssid, len); + wdev->wext.connect.ssid_len = len; + + wdev->wext.connect.crypto.control_port = false; + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); + +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + data->flags = 0; + + if (wdev->ssid_len) { + data->flags = 1; + data->length = wdev->ssid_len; + memcpy(ssid, wdev->ssid, data->length); + } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + data->flags = 1; + data->length = wdev->wext.connect.ssid_len; + memcpy(ssid, wdev->wext.connect.ssid, data->length); + } else + data->flags = 0; + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid); + +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 *bssid = ap_addr->sa_data; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (ap_addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* automatic mode */ + if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) + bssid = NULL; + + /* both automatic */ + if (!bssid && !wdev->wext.connect.bssid) + return 0; + + /* fixed already - and no change */ + if (wdev->wext.connect.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) + return 0; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + false); + if (err) + return err; + } + + if (bssid) { + memcpy(wdev->wext.bssid, bssid, ETH_ALEN); + wdev->wext.connect.bssid = wdev->wext.bssid; + } else + wdev->wext.connect.bssid = NULL; + + return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); + +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + ap_addr->sa_family = ARPHRD_ETHER; + + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + else if (wdev->wext.connect.bssid) + memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap); + +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *ie = extra; + int ie_len = data->length, err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!ie_len) + ie = NULL; + + /* no change */ + if (wdev->wext.ie_len == ie_len && + memcmp(wdev->wext.ie, ie, ie_len) == 0) + return 0; + + if (ie_len) { + ie = kmemdup(extra, ie_len, GFP_KERNEL); + if (!ie) + return -ENOMEM; + } else + ie = NULL; + + kfree(wdev->wext.ie); + wdev->wext.ie = ie; + wdev->wext.ie_len = ie_len; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); + if (err) + return err; + } + + /* userspace better not think we'll reconnect */ + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); + +int cfg80211_wext_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + struct cfg80211_registered_device *rdev; + + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_dev(wdev->wiphy); + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (mlme->addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + case IW_MLME_DISASSOC: + return cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); -- cgit v1.1 From bc92afd92088ab41223383cc6863ab4792533c54 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:57 +0200 Subject: cfg80211: implement iwpower Just on/off and timeout, and with a hacky cfg80211 method until we figure out what we want, though this is probably sufficient as we want to use pm_qos for wifi everywhere. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 16 ----------- net/mac80211/cfg.c | 26 +++++++++++++++++ net/mac80211/mlme.c | 5 ---- net/mac80211/wext.c | 70 ++-------------------------------------------- net/wireless/Kconfig | 16 +++++++++++ net/wireless/core.c | 11 +++++++- net/wireless/wext-compat.c | 60 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 114 insertions(+), 90 deletions(-) (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index ba2643a..41a32cd 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -14,22 +14,6 @@ config MAC80211 comment "CFG80211 needs to be enabled for MAC80211" depends on CFG80211=n -config MAC80211_DEFAULT_PS - bool "enable powersave by default" - depends on MAC80211 - default y - help - This option enables powersave mode by default. - - If this causes your applications to misbehave you should fix your - applications instead -- they need to register their network - latency requirement, see Documentation/power/pm_qos_interface.txt. - -config MAC80211_DEFAULT_PS_VALUE - int - default 1 if MAC80211_DEFAULT_PS - default 0 - menu "Rate control algorithm selection" depends on MAC80211 != n diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 03de402..8c7b2cd 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1388,6 +1388,31 @@ int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) } #endif +static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) + return -EOPNOTSUPP; + + if (enabled == sdata->u.mgd.powersave && + timeout == conf->dynamic_ps_timeout) + return 0; + + sdata->u.mgd.powersave = enabled; + conf->dynamic_ps_timeout = timeout; + + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + + ieee80211_recalc_ps(local, -1); + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1431,4 +1456,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_tx_power = ieee80211_get_tx_power, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + .set_power_mgmt = ieee80211_set_power_mgmt, }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fbb93a7..2a78600 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2360,11 +2360,6 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; hw_flags = sdata->local->hw.flags; - - if (hw_flags & IEEE80211_HW_SUPPORTS_PS) { - ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE; - sdata->local->hw.conf.dynamic_ps_timeout = 500; - } } /* configuration hooks */ diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index d4e61dc..f779298 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -255,72 +255,6 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev, return 0; } -static int ieee80211_ioctl_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, - char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_conf *conf = &local->hw.conf; - int timeout = 0; - bool ps; - - if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) - return -EOPNOTSUPP; - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (wrq->disabled) { - ps = false; - timeout = 0; - goto set; - } - - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitely state all */ - ps = true; - break; - default: /* Otherwise we ignore */ - return -EINVAL; - } - - if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) - return -EINVAL; - - if (wrq->flags & IW_POWER_TIMEOUT) - timeout = wrq->value / 1000; - - set: - if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout) - return 0; - - sdata->u.mgd.powersave = ps; - conf->dynamic_ps_timeout = timeout; - - if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); - - ieee80211_recalc_ps(local, -1); - - return 0; -} - -static int ieee80211_ioctl_giwpower(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); - - wrqu->power.disabled = !sdata->u.mgd.powersave; - - return 0; -} - /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { @@ -436,8 +370,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ - (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */ - (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ + (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */ + (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 0402631..c6031d5 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -26,6 +26,22 @@ config CFG80211_REG_DEBUG If unsure, say N. +config CFG80211_DEFAULT_PS + bool "enable powersave by default" + depends on CFG80211 + default y + help + This option enables powersave mode by default. + + If this causes your applications to misbehave you should fix your + applications instead -- they need to register their network + latency requirement, see Documentation/power/pm_qos_interface.txt. + +config CFG80211_DEFAULT_PS_VALUE + int + default 1 if CFG80211_DEFAULT_PS + default 0 + config CFG80211_DEBUGFS bool "cfg80211 DebugFS entries" depends on CFG80211 && DEBUG_FS diff --git a/net/wireless/core.c b/net/wireless/core.c index e2f80dd..413d291 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -550,12 +550,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, } wdev->netdev = dev; wdev->sme_state = CFG80211_SME_IDLE; + mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_WIRELESS_EXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE; + wdev->wext.ps_timeout = 500; + if (rdev->ops->set_power_mgmt) + if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, + wdev->wext.ps, + wdev->wext.ps_timeout)) { + /* assume this means it's off */ + wdev->wext.ps = false; + } #endif - mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_GOING_DOWN: if (!wdev->ssid_len) diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 02f052f..2e1ab78 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -987,3 +987,63 @@ int cfg80211_wext_giwauth(struct net_device *dev, return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth); + +int cfg80211_wext_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + bool ps = wdev->wext.ps; + int timeout = wdev->wext.ps_timeout; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (!rdev->ops->set_power_mgmt) + return -EOPNOTSUPP; + + if (wrq->disabled) { + ps = false; + } else { + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_ON: /* If not specified */ + case IW_POWER_MODE: /* If set all mask */ + case IW_POWER_ALL_R: /* If explicitely state all */ + ps = true; + break; + default: /* Otherwise we ignore */ + return -EINVAL; + } + + if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) + return -EINVAL; + + if (wrq->flags & IW_POWER_TIMEOUT) + timeout = wrq->value / 1000; + } + + err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); + if (err) + return err; + + wdev->wext.ps = ps; + wdev->wext.ps_timeout = timeout; + + return 0; + +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwpower); + +int cfg80211_wext_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wrq->disabled = !wdev->wext.ps; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwpower); -- cgit v1.1 From ab737a4f7dbe57b12b73f482a7b973bf00b41942 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:58 +0200 Subject: cfg80211: implement IWAP for WDS This implements siocsiwap/giwap for WDS mode. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 11 +++++++++++ net/mac80211/wext.c | 26 ++++---------------------- net/wireless/wext-compat.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8c7b2cd..2cf5bf6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1369,6 +1369,16 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) return 0; } +static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, + u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN); + + return 0; +} + static void ieee80211_rfkill_poll(struct wiphy *wiphy) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -1454,6 +1464,7 @@ struct cfg80211_ops mac80211_config_ops = { .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, + .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) .set_power_mgmt = ieee80211_set_power_mgmt, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index f779298..4053d76 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -140,23 +140,8 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_STATION) return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_WDS) { - /* - * If it is necessary to update the WDS peer address - * while the interface is running, then we need to do - * more work here, namely if it is running we need to - * add a new and remove the old STA entry, this is - * normally handled by _open() and _stop(). - */ - if (netif_running(dev)) - return -EBUSY; - - memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, - ETH_ALEN); - - return 0; - } - + if (sdata->vif.type == NL80211_IFTYPE_WDS) + return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); return -EOPNOTSUPP; } @@ -173,11 +158,8 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_STATION) return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_WDS) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); - return 0; - } + if (sdata->vif.type == NL80211_IFTYPE_WDS) + return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); return -EOPNOTSUPP; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 2e1ab78..2f72dae 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1047,3 +1047,49 @@ int cfg80211_wext_giwpower(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwpower); + +int cfg80211_wds_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int err; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + if (addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (netif_running(dev)) + return -EBUSY; + + if (!rdev->ops->set_wds_peer) + return -EOPNOTSUPP; + + err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); + if (err) + return err; + + memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wds_wext_siwap); + +int cfg80211_wds_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + addr->sa_family = ARPHRD_ETHER; + memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wds_wext_giwap); -- cgit v1.1 From 9930380f0bd8405fa6a51d644f3de88c30666519 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:59 +0200 Subject: cfg80211: implement IWRATE For now, let's implement that using a very hackish way: simply mirror the wext API in the cfg80211 API. This will have to be changed later when we implement proper bitrate API. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 43 ++++++++++++++++++++++++++ net/mac80211/wext.c | 76 ++-------------------------------------------- net/wireless/wext-compat.c | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 74 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2cf5bf6..028f643 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1423,6 +1423,48 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, + const struct cfg80211_bitrate_mask *mask) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int i, err = -EINVAL; + u32 target_rate; + struct ieee80211_supported_band *sband; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + + /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates + * target_rate = X, rate->fixed = 1 means only rate X + * target_rate = X, rate->fixed = 0 means all rates <= X */ + sdata->max_ratectrl_rateidx = -1; + sdata->force_unicast_rateidx = -1; + + if (mask->fixed) + target_rate = mask->fixed / 100; + else if (mask->maxrate) + target_rate = mask->maxrate / 100; + else + return 0; + + for (i=0; i< sband->n_bitrates; i++) { + struct ieee80211_rate *brate = &sband->bitrates[i]; + int this_rate = brate->bitrate; + + if (target_rate == this_rate) { + sdata->max_ratectrl_rateidx = i; + if (mask->fixed) + sdata->force_unicast_rateidx = i; + err = 0; + break; + } + } + + return err; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1468,4 +1510,5 @@ struct cfg80211_ops mac80211_config_ops = { .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) .set_power_mgmt = ieee80211_set_power_mgmt, + .set_bitrate_mask = ieee80211_set_bitrate_mask, }; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 4053d76..244d830 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -165,78 +165,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, } -static int ieee80211_ioctl_siwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - int i, err = -EINVAL; - u32 target_rate = rate->value / 100000; - struct ieee80211_sub_if_data *sdata; - struct ieee80211_supported_band *sband; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates - * target_rate = X, rate->fixed = 1 means only rate X - * target_rate = X, rate->fixed = 0 means all rates <= X */ - sdata->max_ratectrl_rateidx = -1; - sdata->force_unicast_rateidx = -1; - if (rate->value < 0) - return 0; - - for (i=0; i< sband->n_bitrates; i++) { - struct ieee80211_rate *brate = &sband->bitrates[i]; - int this_rate = brate->bitrate; - - if (target_rate == this_rate) { - sdata->max_ratectrl_rateidx = i; - if (rate->fixed) - sdata->force_unicast_rateidx = i; - err = 0; - break; - } - } - return err; -} - -static int ieee80211_ioctl_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct sta_info *sta; - struct ieee80211_sub_if_data *sdata; - struct ieee80211_supported_band *sband; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - rcu_read_lock(); - - sta = sta_info_get(local, sdata->u.mgd.bssid); - - if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) - rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; - else - rate->value = 0; - - rcu_read_unlock(); - - if (!sta) - return -ENODEV; - - rate->value *= 100000; - - return 0; -} - /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { @@ -340,8 +268,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) NULL, /* SIOCGIWNICKN */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ - (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) cfg80211_wext_siwrate, /* SIOCSIWRATE */ + (iw_handler) cfg80211_wext_giwrate, /* SIOCGIWRATE */ (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 2f72dae..3a5f999 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1093,3 +1093,66 @@ int cfg80211_wds_wext_giwap(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wds_wext_giwap); + +int cfg80211_wext_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bitrate_mask mask; + + if (!rdev->ops->set_bitrate_mask) + return -EOPNOTSUPP; + + mask.fixed = 0; + mask.maxrate = 0; + + if (rate->value < 0) { + /* nothing */ + } else if (rate->fixed) { + mask.fixed = rate->value / 1000; /* kbps */ + } else { + mask.maxrate = rate->value / 1000; /* kbps */ + } + + return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); + +int cfg80211_wext_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use a static struct */ + static struct station_info sinfo; + u8 *addr; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + addr = wdev->wext.connect.bssid; + if (!addr) + return -EOPNOTSUPP; + + err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); + if (err) + return err; + + if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) + return -EOPNOTSUPP; + + rate->value = 0; + + if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS)) + rate->value = 100000 * sinfo.txrate.legacy; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwrate); -- cgit v1.1 From 8990646d2fafeacfacba4a4b1073a4216662089a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:27:00 +0200 Subject: cfg80211: implement get_wireless_stats By dropping the noise reporting, we can implement wireless stats in cfg80211. We also make the handler return NULL if we have no information, which is possible thanks to the recent wext change. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wext.c | 69 +--------------------------------------------- net/wireless/wext-compat.c | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 68 deletions(-) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 244d830..5acb814 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -165,73 +165,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, } -/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ -static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct iw_statistics *wstats = &local->wstats; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct sta_info *sta = NULL; - - rcu_read_lock(); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sta = sta_info_get(local, sdata->u.mgd.bssid); - - if (!sta) { - wstats->discard.fragment = 0; - wstats->discard.misc = 0; - wstats->qual.qual = 0; - wstats->qual.level = 0; - wstats->qual.noise = 0; - wstats->qual.updated = IW_QUAL_ALL_INVALID; - } else { - wstats->qual.updated = 0; - /* - * mirror what cfg80211 does for iwrange/scan results, - * otherwise userspace gets confused. - */ - if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | - IEEE80211_HW_SIGNAL_DBM)) { - wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats->qual.updated |= IW_QUAL_QUAL_UPDATED; - } else { - wstats->qual.updated |= IW_QUAL_LEVEL_INVALID; - wstats->qual.updated |= IW_QUAL_QUAL_INVALID; - } - - if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { - wstats->qual.level = sta->last_signal; - wstats->qual.qual = sta->last_signal; - } else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { - int sig = sta->last_signal; - - wstats->qual.updated |= IW_QUAL_DBM; - wstats->qual.level = sig; - if (sig < -110) - sig = -110; - else if (sig > -40) - sig = -40; - wstats->qual.qual = sig + 110; - } - - if (local->hw.flags & IEEE80211_HW_NOISE_DBM) { - /* - * This assumes that if driver reports noise, it also - * reports signal in dBm. - */ - wstats->qual.noise = sta->last_noise; - wstats->qual.updated |= IW_QUAL_NOISE_UPDATED; - } else { - wstats->qual.updated |= IW_QUAL_NOISE_INVALID; - } - } - - rcu_read_unlock(); - - return wstats; -} - /* Structures to export the Wireless Handlers */ static const iw_handler ieee80211_handler[] = @@ -298,5 +231,5 @@ const struct iw_handler_def ieee80211_iw_handler_def = { .num_standard = ARRAY_SIZE(ieee80211_handler), .standard = (iw_handler *) ieee80211_handler, - .get_wireless_stats = ieee80211_get_wireless_stats, + .get_wireless_stats = cfg80211_wireless_stats, }; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 3a5f999..226cf86 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1156,3 +1156,62 @@ int cfg80211_wext_giwrate(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrate); + +/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ +struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use static structs */ + static struct iw_statistics wstats; + static struct station_info sinfo; + u8 *addr; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return NULL; + + if (!rdev->ops->get_station) + return NULL; + + addr = wdev->wext.connect.bssid; + if (!addr) + return NULL; + + if (rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo)) + return NULL; + + memset(&wstats, 0, sizeof(wstats)); + + switch (rdev->wiphy.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + if (sinfo.filled & STATION_INFO_SIGNAL) { + int sig = sinfo.signal; + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.updated |= IW_QUAL_DBM; + wstats.qual.level = sig; + if (sig < -110) + sig = -110; + else if (sig > -40) + sig = -40; + wstats.qual.qual = sig + 110; + break; + } + case CFG80211_SIGNAL_TYPE_UNSPEC: + if (sinfo.filled & STATION_INFO_SIGNAL) { + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.level = sinfo.signal; + wstats.qual.qual = sinfo.signal; + break; + } + default: + wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; + wstats.qual.updated |= IW_QUAL_QUAL_INVALID; + } + + wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + + return &wstats; +} +EXPORT_SYMBOL_GPL(cfg80211_wireless_stats); -- cgit v1.1 From ac00326e9d1d46bf48e9cf60cf892a96b885601a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:27:01 +0200 Subject: mac80211: re-add HT disabling The IEEE80211_STA_TKIP_WEP_USED flag is used internally to disable HT when WEP or TKIP are used. Now that cfg80211 is giving us the required information, we can set the flag appropriately again. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 028f643..af4733b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1228,7 +1228,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { struct ieee80211_sub_if_data *sdata; - int ret; + int ret, i; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1236,6 +1236,14 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ + sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED; + + for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) + if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) + sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED; + sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; -- cgit v1.1 From 6dc1cb0319997648f59b0f46b9e093e779f0353d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:40:45 +0200 Subject: mac80211: remove auth algorithm retry The automatic auth algorithm issue is now solved in cfg80211, so mac80211 no longer needs code to try different algorithms -- just using whatever cfg80211 asked for is good. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 8 ++++---- net/mac80211/debugfs_netdev.c | 3 --- net/mac80211/ieee80211_i.h | 8 -------- net/mac80211/mlme.c | 47 ------------------------------------------- 4 files changed, 4 insertions(+), 62 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index af4733b..3c9774e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1178,16 +1178,16 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; + sdata->u.mgd.auth_alg = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY; + sdata->u.mgd.auth_alg = WLAN_AUTH_SHARED_KEY; break; case NL80211_AUTHTYPE_FT: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT; + sdata->u.mgd.auth_alg = WLAN_AUTH_FT; break; case NL80211_AUTHTYPE_NETWORK_EAP: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP; + sdata->u.mgd.auth_alg = WLAN_AUTH_LEAP; break; default: return -EOPNOTSUPP; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e342032..df8c804 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -105,7 +105,6 @@ IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE); IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC); IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC); -IEEE80211_IF_FILE(auth_algs, u.mgd.auth_algs, HEX); IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC); IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC); @@ -194,7 +193,6 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(extra_ie_len, sta); DEBUGFS_ADD(auth_tries, sta); DEBUGFS_ADD(assoc_tries, sta); - DEBUGFS_ADD(auth_algs, sta); DEBUGFS_ADD(auth_alg, sta); DEBUGFS_ADD(auth_transaction, sta); DEBUGFS_ADD(flags, sta); @@ -327,7 +325,6 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_DEL(extra_ie_len, sta); DEBUGFS_DEL(auth_tries, sta); DEBUGFS_DEL(assoc_tries, sta); - DEBUGFS_DEL(auth_algs, sta); DEBUGFS_DEL(auth_alg, sta); DEBUGFS_DEL(auth_transaction, sta); DEBUGFS_DEL(flags, sta); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 1950e2e..f7024ee 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -251,12 +251,6 @@ struct mesh_preq_queue { #define IEEE80211_STA_REQ_AUTH 1 #define IEEE80211_STA_REQ_RUN 2 -/* bitfield of allowed auth algs */ -#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; struct timer_list chswitch_timer; @@ -303,7 +297,6 @@ struct ieee80211_if_managed { unsigned int flags; - unsigned int auth_algs; /* bitfield of allowed auth algs */ int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ int auth_transaction; @@ -488,7 +481,6 @@ struct ieee80211_sub_if_data { struct dentry *extra_ie_len; struct dentry *auth_tries; struct dentry *assoc_tries; - struct dentry *auth_algs; struct dentry *auth_alg; struct dentry *auth_transaction; struct dentry *flags; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2a78600..2b357c7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1414,39 +1414,6 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, return; if (status_code != WLAN_STATUS_SUCCESS) { - if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - u8 algs[3]; - const int num_algs = ARRAY_SIZE(algs); - int i, pos; - algs[0] = algs[1] = algs[2] = 0xff; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN) - algs[0] = WLAN_AUTH_OPEN; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) - algs[1] = WLAN_AUTH_SHARED_KEY; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) - algs[2] = WLAN_AUTH_LEAP; - if (ifmgd->auth_alg == WLAN_AUTH_OPEN) - pos = 0; - else if (ifmgd->auth_alg == WLAN_AUTH_SHARED_KEY) - pos = 1; - else - pos = 2; - for (i = 0; i < num_algs; i++) { - pos++; - if (pos >= num_algs) - pos = 0; - if (algs[pos] == ifmgd->auth_alg || - algs[pos] == 0xff) - continue; - if (algs[pos] == WLAN_AUTH_SHARED_KEY && - !ieee80211_sta_wep_configured(sdata)) - continue; - ifmgd->auth_alg = algs[pos]; - ifmgd->auth_tries = 0; - return; - } - } - /* nothing else to try -- give up */ cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); ifmgd->state = IEEE80211_STA_MLME_DISABLED; @@ -2102,18 +2069,6 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) drv_reset_tsf(local); ifmgd->wmm_last_param_set = -1; /* allow any WMM update */ - - - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN) - ifmgd->auth_alg = WLAN_AUTH_OPEN; - else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) - 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; ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED; ifmgd->assoc_scan_tries = 0; @@ -2351,8 +2306,6 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) skb_queue_head_init(&ifmgd->skb_queue); ifmgd->capab = WLAN_CAPABILITY_ESS; - ifmgd->auth_algs = IEEE80211_AUTH_ALG_OPEN | - IEEE80211_AUTH_ALG_SHARED_KEY; ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | IEEE80211_STA_AUTO_BSSID_SEL | IEEE80211_STA_AUTO_CHANNEL_SEL; -- cgit v1.1 From ab1faead50d09165b58c2854997c7205ca9c0d22 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:41:17 +0200 Subject: mac80211: remove dead code, clean up With mac80211 now always controlled by an external SME, a lot of code is dead -- SSID, BSSID, channel selection is always done externally, etc. Additionally, rename IEEE80211_STA_TKIP_WEP_USED to IEEE80211_STA_DISABLE_11N and clean up the code a bit. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 39 +++---- net/mac80211/debugfs_netdev.c | 4 +- net/mac80211/ieee80211_i.h | 42 ++++---- net/mac80211/iface.c | 2 - net/mac80211/mlme.c | 231 ++++-------------------------------------- 5 files changed, 54 insertions(+), 264 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3c9774e..7606571 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1194,18 +1194,14 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, } 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; + sdata->local->oper_channel = req->chan; + ieee80211_hw_config(sdata->local, 0); - 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; - } + if (!req->ssid) + return -EINVAL; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; kfree(sdata->u.mgd.sme_auth_ie); sdata->u.mgd.sme_auth_ie = NULL; @@ -1218,7 +1214,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, 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; @@ -1236,27 +1231,22 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ - sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED; + sdata->u.mgd.flags &= ~IEEE80211_STA_DISABLE_11N; for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) - sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED; + sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_11N; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; + sdata->local->oper_channel = req->chan; + ieee80211_hw_config(sdata->local, 0); - /* TODO: req->chan */ - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; + if (!req->ssid) + return -EINVAL; - 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; + memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); + sdata->u.mgd.ssid_len = req->ssid_len; ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); if (ret && ret != -EALREADY) @@ -1275,7 +1265,6 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, else sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; ieee80211_sta_req_auth(sdata); return 0; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index df8c804..ffe9add 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -111,9 +111,7 @@ IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC); static ssize_t ieee80211_if_fmt_flags( const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { - return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n", - sdata->u.mgd.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "", + return scnprintf(buf, buflen, "%s%s%s%s%s\n", sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "", sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "", sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f7024ee..a550aeb 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -228,28 +228,24 @@ struct mesh_preq_queue { }; /* flags used in struct ieee80211_if_managed.flags */ -#define IEEE80211_STA_SSID_SET BIT(0) -#define IEEE80211_STA_BSSID_SET BIT(1) -#define IEEE80211_STA_PREV_BSSID_SET BIT(2) -#define IEEE80211_STA_AUTHENTICATED BIT(3) -#define IEEE80211_STA_ASSOCIATED BIT(4) -#define IEEE80211_STA_PROBEREQ_POLL BIT(5) -#define IEEE80211_STA_CREATE_IBSS BIT(6) -#define IEEE80211_STA_CONTROL_PORT BIT(7) -#define IEEE80211_STA_WMM_ENABLED BIT(8) -/* hole at 9, please re-use */ -#define IEEE80211_STA_AUTO_SSID_SEL BIT(10) -#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11) -#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) -#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) -#define IEEE80211_STA_EXT_SME BIT(17) +enum ieee80211_sta_flags { + IEEE80211_STA_PREV_BSSID_SET = BIT(0), + IEEE80211_STA_AUTHENTICATED = BIT(1), + IEEE80211_STA_ASSOCIATED = BIT(2), + IEEE80211_STA_PROBEREQ_POLL = BIT(3), + IEEE80211_STA_CONTROL_PORT = BIT(4), + IEEE80211_STA_WMM_ENABLED = BIT(5), + IEEE80211_STA_DISABLE_11N = BIT(6), + IEEE80211_STA_CSA_RECEIVED = BIT(7), + IEEE80211_STA_MFP_ENABLED = BIT(8), +}; + /* flags for MLME request */ -#define IEEE80211_STA_REQ_SCAN 0 -#define IEEE80211_STA_REQ_AUTH 1 -#define IEEE80211_STA_REQ_RUN 2 +enum ieee80211_sta_request { + IEEE80211_STA_REQ_SCAN, + IEEE80211_STA_REQ_AUTH, + IEEE80211_STA_REQ_RUN, +}; struct ieee80211_if_managed { struct timer_list timer; @@ -936,10 +932,6 @@ extern const struct iw_handler_def ieee80211_iw_handler_def; void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata); -int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len); -int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len); -int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid); void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata); int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason); int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b7c8a44..b87bf42 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -486,8 +486,6 @@ static int ieee80211_stop(struct net_device *dev) synchronize_rcu(); skb_queue_purge(&sdata->u.mgd.skb_queue); - sdata->u.mgd.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED | - IEEE80211_STA_TKIP_WEP_USED); kfree(sdata->u.mgd.extra_ie); sdata->u.mgd.extra_ie = NULL; sdata->u.mgd.extra_ie_len = 0; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2b357c7..2d9b666 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -347,7 +347,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) sband->ht_cap.ht_supported && (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) && ht_ie[1] >= sizeof(struct ieee80211_ht_info) && - (!(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))) { + (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) { struct ieee80211_ht_info *ht_info = (struct ieee80211_ht_info *)(ht_ie + 2); u16 cap = sband->ht_cap.cap; @@ -981,8 +981,6 @@ 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) { @@ -1010,15 +1008,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: authenticate with AP %pM\n", sdata->dev->name, ifmgd->bssid); - 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); + ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ifmgd->sme_auth_ie, + ifmgd->sme_auth_ie_len, ifmgd->bssid, 0); ifmgd->auth_transaction = 2; mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); @@ -1128,44 +1119,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sta_info_destroy(sta); } -static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata) -{ - if (!sdata || !sdata->default_key || - sdata->default_key->conf.alg != ALG_WEP) - return 0; - return 1; -} - -static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct ieee80211_bss *bss; - int bss_privacy; - int wep_privacy; - int privacy_invoked; - - if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME)) - return 0; - - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (!bss) - return 0; - - bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY); - wep_privacy = !!ieee80211_sta_wep_configured(sdata); - privacy_invoked = !!(ifmgd->flags & IEEE80211_STA_PRIVACY_INVOKED); - - ieee80211_rx_bss_put(local, bss); - - if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked)) - return 0; - - return 1; -} - static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -1195,14 +1148,6 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; printk(KERN_DEBUG "%s: associate with AP %pM\n", sdata->dev->name, ifmgd->bssid); - if (ieee80211_privacy_mismatch(sdata)) { - printk(KERN_DEBUG "%s: mismatch in privacy configuration and " - "mixed-cell disabled - abort association\n", sdata->dev->name); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - return; - } - ieee80211_send_assoc(sdata); mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); @@ -1360,12 +1305,9 @@ 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; - if (ifmgd->flags & IEEE80211_STA_EXT_SME) { - /* Wait for SME to request association */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(sdata->local); - } else - ieee80211_associate(sdata); + /* Wait for SME to request association */ + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + ieee80211_recalc_idle(sdata->local); } @@ -1460,15 +1402,6 @@ 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->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); - } - ieee80211_set_disassoc(sdata, true, false, 0); ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); @@ -1494,13 +1427,6 @@ 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->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); - } - ieee80211_set_disassoc(sdata, false, false, reason_code); cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); } @@ -1573,11 +1499,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); - if (ifmgd->flags & IEEE80211_STA_EXT_SME) { - /* Wait for SME to decide what to do next */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - } + /* Wait for SME to decide what to do next */ + ifmgd->state = IEEE80211_STA_MLME_DISABLED; + ieee80211_recalc_idle(local); return; } @@ -1683,8 +1607,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; - /* If TKIP/WEP is used, no need to parse AP's HT capabilities */ - if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) + if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_ht_cap_ie_to_sta_ht_cap(sband, elems.ht_cap_elem, &sta->sta.ht_cap); @@ -1718,7 +1641,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (elems.ht_info_elem && elems.wmm_param && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && - !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) + !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, ap_ht_cap_flags); @@ -1931,7 +1854,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) { + !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { struct sta_info *sta; struct ieee80211_supported_band *sband; u16 ap_ht_cap_flags; @@ -2090,26 +2013,6 @@ 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_EXT_SME) && - ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | - IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL)) { - capa_mask |= WLAN_CAPABILITY_PRIVACY; - if (sdata->default_key) - capa_val |= WLAN_CAPABILITY_PRIVACY; - } - - if (ifmgd->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) - chan = NULL; - - if (ifmgd->flags & IEEE80211_STA_AUTO_BSSID_SEL) - bssid = NULL; - - if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL) { - ssid = NULL; - ssid_len = 0; - } - bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid, ssid, ssid_len, capa_mask, capa_val); @@ -2119,10 +2022,6 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, 0); - if (!(ifmgd->flags & IEEE80211_STA_SSID_SET)) - ieee80211_sta_set_ssid(sdata, bss->ssid, - bss->ssid_len); - ieee80211_sta_set_bssid(sdata, bss->cbss.bssid); ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len, bss->supp_rates); if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED) @@ -2232,14 +2131,6 @@ static void ieee80211_sta_work(struct work_struct *work) WARN_ON(1); break; } - - if (ieee80211_privacy_mismatch(sdata)) { - printk(KERN_DEBUG "%s: privacy configuration mismatch and " - "mixed-cell disabled - disassociate\n", sdata->dev->name); - - ieee80211_set_disassoc(sdata, false, true, - WLAN_REASON_UNSPECIFIED); - } } static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) @@ -2306,9 +2197,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) skb_queue_head_init(&ifmgd->skb_queue); ifmgd->capab = WLAN_CAPABILITY_ESS; - ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | - IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL; + ifmgd->flags = 0; if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; @@ -2324,96 +2213,20 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; - if ((ifmgd->flags & (IEEE80211_STA_BSSID_SET | - IEEE80211_STA_AUTO_BSSID_SEL)) && - (ifmgd->flags & (IEEE80211_STA_SSID_SET | - IEEE80211_STA_AUTO_SSID_SEL))) { - - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); - - if (ifmgd->ssid_len == 0) { - /* - * Only allow association to be started if a valid SSID - * is configured. - */ - return; - } - - 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); - } -} - -int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - if (ifmgd->ssid_len) - ifmgd->flags |= IEEE80211_STA_SSID_SET; - else - ifmgd->flags &= ~IEEE80211_STA_SSID_SET; - - return 0; -} - -int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len) -{ - struct ieee80211_if_managed *ifmgd; - - if (len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - - ifmgd = &sdata->u.mgd; - - if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) { - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); - - /* - * Do not use reassociation if SSID is changed (different ESS). - */ - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid)); - memcpy(ifmgd->ssid, ssid, len); - ifmgd->ssid_len = len; - } - - return ieee80211_sta_commit(sdata); -} - -int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - memcpy(ssid, ifmgd->ssid, ifmgd->ssid_len); - *len = ifmgd->ssid_len; - return 0; -} - -int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - if (compare_ether_addr(bssid, ifmgd->bssid) != 0 && - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) + if (WARN_ON(ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) ieee80211_set_disassoc(sdata, true, true, WLAN_REASON_DEAUTH_LEAVING); - if (is_valid_ether_addr(bssid)) { - memcpy(ifmgd->bssid, bssid, ETH_ALEN); - ifmgd->flags |= IEEE80211_STA_BSSID_SET; - } else { - memset(ifmgd->bssid, 0, ETH_ALEN); - ifmgd->flags &= ~IEEE80211_STA_BSSID_SET; + if (WARN_ON(ifmgd->ssid_len == 0)) { + /* + * Only allow association to be started if a valid SSID + * is configured. + */ + return; } - return ieee80211_sta_commit(sdata); + set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); + queue_work(local->hw.workqueue, &ifmgd->work); } int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, -- cgit v1.1 From e45cd82ace91b71bc690ba78a0ebea17edfaabef Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 09:58:04 +0200 Subject: cfg80211: send events for userspace SME When the userspace SME is in control, we are currently not sending events, but this means that any userspace applications using wext or nl80211 to receive events will not know what's going on unless they can also interpret the nl80211 assoc event. Since we have all the required code, let the SME follow events from the userspace SME, this even means that you will be refused to connect() while the userspace SME is in control and connected. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 64 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f272ebf..d4e0b40 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -287,6 +287,44 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; + if (wdev->sme_state == CFG80211_SME_CONNECTED) + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, + bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, gfp); + else + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, + bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, + status, gfp); + +#ifdef CONFIG_WIRELESS_EXT + if (wextev) { + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid && status == WLAN_STATUS_SUCCESS) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + } +#endif + + if (status == WLAN_STATUS_SUCCESS && + wdev->sme_state == CFG80211_SME_IDLE) { + wdev->sme_state = CFG80211_SME_CONNECTED; + return; + } + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -315,32 +353,6 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; - - nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, - status, gfp); - -#ifdef CONFIG_WIRELESS_EXT - if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); - } - - if (resp_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); - } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - } -#endif } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, -- cgit v1.1 From 0eb14647fc6b56f73efb28a72ca0c7cc03a653f9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 15:49:03 +0200 Subject: cfg80211: reset auth algorithm When the interface is brought down, we need to reset the auth algorithm because wpa_supplicant doesn't reset it, and then we fail to use shared key auth when required later. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 413d291..5f6a832 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -579,6 +579,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, kfree(wdev->wext.ie); wdev->wext.ie = NULL; wdev->wext.ie_len = 0; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); -- cgit v1.1 From 517357c685ccc4b5783cc7dbdae8824ada19a97f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 17:18:40 +0200 Subject: cfg80211: assimilate and export ieee80211_bss_get_ie This function from mac80211 seems generally useful, and I will need it in cfg80211 soon. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 25 +++---------------------- net/wireless/util.c | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2d9b666..5748cda 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -46,26 +46,6 @@ static int ecw2cw(int ecw) return (1 << ecw) - 1; } -static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie) -{ - u8 *end, *pos; - - pos = bss->cbss.information_elements; - if (pos == NULL) - return NULL; - end = pos + bss->cbss.len_information_elements; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - static int ieee80211_compatible_rates(struct ieee80211_bss *bss, struct ieee80211_supported_band *sband, u32 *rates) @@ -181,7 +161,8 @@ 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; + const u8 *ies, *ht_ie; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; @@ -345,7 +326,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) */ if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && sband->ht_cap.ht_supported && - (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) && + (ht_ie = ieee80211_bss_get_ie(&bss->cbss, WLAN_EID_HT_INFORMATION)) && ht_ie[1] >= sizeof(struct ieee80211_ht_info) && (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) { struct ieee80211_ht_info *ht_info = diff --git a/net/wireless/util.c b/net/wireless/util.c index 2555069..28f8f96 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -502,3 +502,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb) return dscp >> 5; } EXPORT_SYMBOL(cfg80211_classify8021d); + +const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +{ + u8 *end, *pos; + + pos = bss->information_elements; + if (pos == NULL) + return NULL; + end = pos + bss->len_information_elements; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} +EXPORT_SYMBOL(ieee80211_bss_get_ie); -- cgit v1.1 From 19957bb399e2722719c0e20c9ae91cf8b6aaff04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 17:20:43 +0200 Subject: cfg80211: keep track of BSSes In order to avoid problems with BSS structs going away while they're in use, I've long wanted to make cfg80211 keep track of them. Without the SME, that wasn't doable but now that we have the SME we can do this too. It can keep track of up to four separate authentications and one association, regardless of whether it's controlled by the cfg80211 SME or the userspace SME. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 22 ++- net/mac80211/mlme.c | 6 +- net/wireless/core.c | 5 +- net/wireless/core.h | 41 +++++- net/wireless/ibss.c | 12 +- net/wireless/mlme.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++-- net/wireless/nl80211.c | 144 +++++++++---------- net/wireless/scan.c | 31 +---- net/wireless/sme.c | 156 ++++++++++++--------- net/wireless/wext-sme.c | 4 +- 10 files changed, 567 insertions(+), 211 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7606571..0f29cd0 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1173,6 +1173,7 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { struct ieee80211_sub_if_data *sdata; + const u8 *ssid; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1193,15 +1194,16 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, return -EOPNOTSUPP; } - memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); + memcpy(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN); - sdata->local->oper_channel = req->chan; + sdata->local->oper_channel = req->bss->channel; ieee80211_hw_config(sdata->local, 0); - if (!req->ssid) + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + if (!ssid) return -EINVAL; - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; + sdata->u.mgd.ssid_len = *(ssid + 1); + memcpy(sdata->u.mgd.ssid, ssid + 2, sdata->u.mgd.ssid_len); kfree(sdata->u.mgd.sme_auth_ie); sdata->u.mgd.sme_auth_ie = NULL; @@ -1227,7 +1229,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || + if (memcmp(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN) != 0 || !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) return -ENOLINK; /* not authenticated */ @@ -1239,15 +1241,9 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_11N; - sdata->local->oper_channel = req->chan; + sdata->local->oper_channel = req->bss->channel; ieee80211_hw_config(sdata->local, 0); - if (!req->ssid) - return -EINVAL; - - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; - ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); if (ret && ret != -EALREADY) return ret; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5748cda..aa1829a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -876,8 +876,6 @@ 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); } @@ -1031,10 +1029,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, conf->channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - cfg80211_unhold_bss(&bss->cbss); + if (bss) ieee80211_rx_bss_put(local, bss); - } if (self_disconnected) { if (deauth) diff --git a/net/wireless/core.c b/net/wireless/core.c index 5f6a832..7b66cf1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -583,15 +583,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_mlme_down(rdev, dev); break; default: break; } break; - case NETDEV_DOWN: - kfree(wdev->conn); - wdev->conn = NULL; - break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT switch (wdev->iftype) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 5209acb..82918f5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -110,12 +110,30 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; - bool hold, ies_allocated; + atomic_t hold; + bool ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; }; +static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) +{ + return container_of(pub, struct cfg80211_internal_bss, pub); +} + +static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) +{ + atomic_inc(&bss->hold); +} + +static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) +{ + int r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); +} + + struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); @@ -176,6 +194,26 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +/* MLME */ +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* SME */ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -193,5 +231,6 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_sme_disassoc(struct net_device *dev, int idx); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 34b11ea..c92b542 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -33,11 +33,11 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); #ifdef CONFIG_WIRELESS_EXT @@ -78,7 +78,7 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; @@ -212,7 +212,7 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, return -EINVAL; if (wdev->current_bss) - chan = wdev->current_bss->channel; + chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; @@ -352,7 +352,7 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); return 0; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 3427fe7..1a92bf7 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -14,8 +14,32 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *bssid = mgmt->bssid; + int i; + u16 status = le16_to_cpu(mgmt->u.auth.status_code); + bool done = false; + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0) { + if (status == WLAN_STATUS_SUCCESS) { + wdev->auth_bsses[i] = wdev->authtry_bsses[i]; + } else { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + } + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); nl80211_send_rx_auth(rdev, dev, buf, len, gfp); cfg80211_sme_rx_auth(dev, buf, len); @@ -30,7 +54,8 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; - int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + bool done; status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -38,6 +63,20 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, gfp); + + if (status_code == WLAN_STATUS_SUCCESS) { + for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] == wdev->current_bss) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + } } EXPORT_SYMBOL(cfg80211_send_rx_assoc); @@ -47,9 +86,45 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + bool done = false; nl80211_send_deauth(rdev, dev, buf, len, gfp); + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + done = true; + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } +/* + * mac80211 currently triggers this warning, + * so disable for now (it's harmless, just + * means that we got a spurious event) + + WARN_ON(!done); + + */ + if (wdev->sme_state == CFG80211_SME_CONNECTED) { u16 reason_code; bool from_ap; @@ -59,8 +134,6 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; __cfg80211_disconnected(dev, gfp, NULL, 0, reason_code, from_ap); - - wdev->sme_state = CFG80211_SME_IDLE; } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); @@ -74,21 +147,38 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, g struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + u16 reason_code; + bool from_ap; + bool done = false; nl80211_send_disassoc(rdev, dev, buf, len, gfp); - if (wdev->sme_state == CFG80211_SME_CONNECTED) { - u16 reason_code; - bool from_ap; + if (!wdev->sme_state == CFG80211_SME_CONNECTED) + return; - reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + if (wdev->current_bss && + memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) + continue; + wdev->auth_bsses[i] = wdev->current_bss; + wdev->current_bss = NULL; + done = true; + cfg80211_sme_disassoc(dev, i); + break; + } + WARN_ON(!done); + } else + WARN_ON(1); - from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, gfp, NULL, 0, - reason_code, from_ap); - wdev->sme_state = CFG80211_SME_IDLE; - } + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, gfp, NULL, 0, + reason_code, from_ap); } EXPORT_SYMBOL(cfg80211_send_disassoc); @@ -97,11 +187,27 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gf struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int i; + bool done = false; + nl80211_send_auth_timeout(rdev, dev, addr, gfp); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); - wdev->sme_state = CFG80211_SME_IDLE; + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -110,11 +216,27 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t g struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + int i; + bool done = false; + nl80211_send_assoc_timeout(rdev, dev, addr, gfp); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); - wdev->sme_state = CFG80211_SME_IDLE; + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); @@ -143,3 +265,208 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); + +/* some MLME handling for userspace SME */ +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_auth_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1, nfree = 0; + + memset(&req, 0, sizeof(req)); + + req.ie = ie; + req.ie_len = ie_len; + req.auth_type = auth_type; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + err = -EALREADY; + goto out; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { + slot = i; + nfree++; + } + } + + /* we need one free slot for disassoc and one for this auth */ + if (nfree < 2) { + err = -ENOSPC; + goto out; + } + + wdev->authtry_bsses[slot] = bss; + cfg80211_hold_bss(bss); + + err = rdev->ops->auth(&rdev->wiphy, dev, &req); + if (err) { + wdev->authtry_bsses[slot] = NULL; + cfg80211_unhold_bss(bss); + } + + out: + if (err) + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_assoc_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1; + + memset(&req, 0, sizeof(req)); + + if (wdev->current_bss) + return -EALREADY; + + req.ie = ie; + req.ie_len = ie_len; + memcpy(&req.crypto, crypt, sizeof(req.crypto)); + req.use_mfp = use_mfp; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + err = -ENOTCONN; + goto out; + } + + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); + out: + /* still a reference in wdev->auth_bsses[slot] */ + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + req.bss = &wdev->current_bss->pub; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->auth_bsses[i]->pub; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->authtry_bsses[i]->pub; + break; + } + } + + if (!req.bss) + return -ENOTCONN; + + return rdev->ops->deauth(&rdev->wiphy, dev, &req); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_disassoc_request req; + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) + req.bss = &wdev->current_bss->pub; + else + return -ENOTCONN; + + return rdev->ops->disassoc(&rdev->wiphy, dev, &req); +} + +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + if (!rdev->ops->deauth) + return; + + memset(&req, 0, sizeof(req)); + req.reason_code = WLAN_REASON_DEAUTH_LEAVING; + req.ie = NULL; + req.ie_len = 0; + + if (wdev->current_bss) { + req.bss = &wdev->current_bss->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i]) { + req.bss = &wdev->auth_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->auth_bsses[i]) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + } + } + if (wdev->authtry_bsses[i]) { + req.bss = &wdev->authtry_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req); + if (wdev->authtry_bsses[i]) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + } + } + } +} diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0008144..aa2b3f3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3044,9 +3044,10 @@ 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; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + enum nl80211_auth_type auth_type; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3057,6 +3058,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) return -EINVAL; + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + rtnl_lock(); err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); @@ -3078,38 +3085,30 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) 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; - } + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + chan = ieee80211_get_channel(&drv->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + 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]); - } + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + 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]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(req.auth_type)) { + auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(auth_type)) { err = -EINVAL; goto out; } - err = drv->ops->auth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); out: cfg80211_put_dev(drv); @@ -3183,26 +3182,29 @@ static int nl80211_crypto_settings(struct genl_info *info, static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_assoc_request req; - struct wiphy *wiphy; - int err; + struct cfg80211_crypto_settings crypto; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + bool use_mfp = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC] || - !info->attrs[NL80211_ATTR_SSID]) + !info->attrs[NL80211_ATTR_SSID] || + !info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->assoc) { + if (!rdev->ops->assoc) { err = -EOPNOTSUPP; goto out; } @@ -3217,46 +3219,42 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = 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; - } + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + err = -EINVAL; + goto out; } - req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + 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]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_USE_MFP]) { enum nl80211_mfp use_mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); if (use_mfp == NL80211_MFP_REQUIRED) - req.use_mfp = true; + use_mfp = true; else if (use_mfp != NL80211_MFP_NO) { err = -EINVAL; goto out; } } - err = nl80211_crypto_settings(info, &req.crypto); + err = nl80211_crypto_settings(info, &crypto); if (!err) - err = drv->ops->assoc(&drv->wiphy, dev, &req); + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, + ssid_len, ie, ie_len, use_mfp, + &crypto); out: - cfg80211_put_dev(drv); + cfg80211_put_dev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3267,9 +3265,9 @@ 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; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3301,24 +3299,21 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } 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]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->deauth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); out: cfg80211_put_dev(drv); @@ -3332,9 +3327,9 @@ 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; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3366,24 +3361,21 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } 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]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->disassoc(&drv->wiphy, dev, &req); + err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); out: cfg80211_put_dev(drv); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 82b33e7..9253994 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -70,6 +70,8 @@ static void bss_release(struct kref *ref) if (bss->ies_allocated) kfree(bss->pub.information_elements); + BUG_ON(atomic_read(&bss->hold)); + kfree(bss); } @@ -92,8 +94,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (bss->hold || - !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (atomic_read(&bss->hold)) + continue; + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -553,30 +556,6 @@ 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, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d4e0b40..412161f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -103,44 +103,37 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); - union { - struct cfg80211_auth_request auth_req; - struct cfg80211_assoc_request assoc_req; - } u; - - memset(&u, 0, sizeof(u)); + struct cfg80211_connect_params *params; + int err; if (!wdev->conn) return 0; + params = &wdev->conn->params; + switch (wdev->conn->state) { case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: - u.auth_req.chan = wdev->conn->params.channel; - u.auth_req.peer_addr = wdev->conn->params.bssid; - u.auth_req.ssid = wdev->conn->params.ssid; - u.auth_req.ssid_len = wdev->conn->params.ssid_len; - u.auth_req.auth_type = wdev->conn->params.auth_type; - u.auth_req.ie = NULL; - u.auth_req.ie_len = 0; - wdev->conn->state = CFG80211_CONN_AUTHENTICATING; BUG_ON(!drv->ops->auth); - return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + return cfg80211_mlme_auth(drv, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: - u.assoc_req.chan = wdev->conn->params.channel; - u.assoc_req.peer_addr = wdev->conn->params.bssid; - u.assoc_req.ssid = wdev->conn->params.ssid; - u.assoc_req.ssid_len = wdev->conn->params.ssid_len; - u.assoc_req.ie = wdev->conn->params.ie; - u.assoc_req.ie_len = wdev->conn->params.ie_len; - u.assoc_req.use_mfp = false; - memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, - sizeof(u.assoc_req.crypto)); - wdev->conn->state = CFG80211_CONN_ASSOCIATING; BUG_ON(!drv->ops->assoc); - return drv->ops->assoc(wdev->wiphy, wdev->netdev, - &u.assoc_req); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + err = cfg80211_mlme_assoc(drv, wdev->netdev, + params->channel, params->bssid, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); + if (err) + cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING); + return err; default: return 0; } @@ -186,7 +179,6 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) wdev->conn->params.ssid_len, WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, capa); - if (!bss) return false; @@ -264,9 +256,11 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); - } else if (status_code != WLAN_STATUS_SUCCESS) + } else if (status_code != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; - else if (wdev->sme_state == CFG80211_SME_CONNECTING && + kfree(wdev->conn); + wdev->conn = NULL; + } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); @@ -330,10 +324,13 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; } + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + if (status == WLAN_STATUS_SUCCESS) { bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, @@ -343,16 +340,15 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(!bss)) return; - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); wdev->sme_state = CFG80211_SME_CONNECTED; } else { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; } - - if (wdev->conn) - wdev->conn->state = CFG80211_CONN_IDLE; } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -387,7 +383,7 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, } cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, @@ -397,8 +393,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, if (WARN_ON(!bss)) return; - cfg80211_hold_bss(bss); - wdev->current_bss = bss; + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); @@ -440,7 +436,7 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; @@ -449,6 +445,8 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, if (wdev->conn) { kfree(wdev->conn->ie); wdev->conn->ie = NULL; + kfree(wdev->conn); + wdev->conn = NULL; } nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, @@ -482,12 +480,12 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; - if (!wdev->conn) { - wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); - if (!wdev->conn) - return -ENOMEM; - } else - memset(wdev->conn, 0, sizeof(*wdev->conn)); + if (WARN_ON(wdev->conn)) + return -EINPROGRESS; + + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; /* * Copy all parameters, and treat explicitly IEs, BSSID, SSID. @@ -502,8 +500,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, GFP_KERNEL); wdev->conn->params.ie = wdev->conn->ie; - if (!wdev->conn->ie) + if (!wdev->conn->ie) { + kfree(wdev->conn); + wdev->conn = NULL; return -ENOMEM; + } } if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { @@ -543,8 +544,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; } } - if (err) + if (err) { + kfree(wdev->conn); + wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; + } return err; } else { @@ -572,31 +576,27 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, return -EINVAL; if (!rdev->ops->disconnect) { - struct cfg80211_deauth_request deauth; - u8 bssid[ETH_ALEN]; + if (!rdev->ops->deauth) + return -EOPNOTSUPP; - /* internal bug. */ - if (WARN_ON(!wdev->conn)) - return -EINVAL; + /* was it connected by userspace SME? */ + if (!wdev->conn) { + cfg80211_mlme_down(rdev, dev); + return 0; + } if (wdev->sme_state == CFG80211_SME_CONNECTING && (wdev->conn->state == CFG80211_CONN_SCANNING || wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; return 0; } - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - memset(&deauth, 0, sizeof(deauth)); - /* wdev->conn->params.bssid must be set if > SCANNING */ - memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); - deauth.peer_addr = bssid; - deauth.reason_code = reason; - - err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); + err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, + NULL, 0, reason); if (err) return err; } else { @@ -614,3 +614,33 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, return 0; } + +void cfg80211_sme_disassoc(struct net_device *dev, int idx) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 bssid[ETH_ALEN]; + + if (!wdev->conn) + return; + + if (wdev->conn->state == CFG80211_CONN_IDLE) + return; + + /* + * Ok, so the association was made by this SME -- we don't + * want it any more so deauthenticate too. + */ + + if (!wdev->auth_bsses[idx]) + return; + + memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); + if (cfg80211_mlme_deauth(rdev, dev, bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { + /* whatever -- assume gone anyway */ + cfg80211_unhold_bss(wdev->auth_bsses[idx]); + cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); + wdev->auth_bsses[idx] = NULL; + } +} diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 3b531d5..fe1987a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -93,7 +93,7 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, return -EINVAL; if (wdev->current_bss) - chan = wdev->current_bss->channel; + chan = wdev->current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; @@ -244,7 +244,7 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN); + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else if (wdev->wext.connect.bssid) memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); else -- cgit v1.1 From 0a9b5e17952ec7ce938645a23ba29bcfdb66c8d9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 18:26:18 +0200 Subject: cfg80211: refuse authenticating to same BSSID twice It is possible that there are different BSS structs with the same BSSID, but we cannot authenticate with multiple of them them because we need the BSSID to be unique for deauthenticating/disassociating. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1a92bf7..020f33b 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -278,6 +278,21 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *bss; int i, err, slot = -1, nfree = 0; + if (wdev->current_bss && + memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) + return -EALREADY; + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + } + memset(&req, 0, sizeof(req)); req.ie = ie; @@ -291,13 +306,6 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, bss = bss_from_pub(req.bss); for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (bss == wdev->auth_bsses[i]) { - err = -EALREADY; - goto out; - } - } - - for (i = 0; i < MAX_AUTH_BSSES; i++) { if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { slot = i; nfree++; -- cgit v1.1 From 3dc27d25f288c923735e8f10fa8e578be896e96a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Jul 2009 21:36:37 +0200 Subject: nl80211: limit to one pairwise cipher for associate() In this case, only one cipher makes sense, unlike for connect() where it may be possible to have the card or driver select. No changes to mac80211 due to the way the structs are laid out -- but the loop in net/mac80211/cfg.c will degrade to just zero or one passes. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index aa2b3f3..723512b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3119,7 +3119,8 @@ unlock_rtnl: } static int nl80211_crypto_settings(struct genl_info *info, - struct cfg80211_crypto_settings *settings) + struct cfg80211_crypto_settings *settings, + int cipher_limit) { settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; @@ -3134,7 +3135,7 @@ static int nl80211_crypto_settings(struct genl_info *info, if (len % sizeof(u32)) return -EINVAL; - if (settings->n_ciphers_pairwise > NL80211_MAX_NR_CIPHER_SUITES) + if (settings->n_ciphers_pairwise > cipher_limit) return -EINVAL; memcpy(settings->ciphers_pairwise, data, len); @@ -3247,7 +3248,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } - err = nl80211_crypto_settings(info, &crypto); + err = nl80211_crypto_settings(info, &crypto, 1); if (!err) err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, ssid_len, ie, ie_len, use_mfp, @@ -3652,7 +3653,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; - err = nl80211_crypto_settings(info, &connect.crypto); + err = nl80211_crypto_settings(info, &connect.crypto, + NL80211_MAX_NR_CIPHER_SUITES); if (err) return err; rtnl_lock(); -- cgit v1.1 From 9834c079d130217c8c5ac8791428ebeb8c660538 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jul 2009 19:40:51 +0200 Subject: cfg80211: fix giwrange "cfg80211: Advertise ciphers via WE according to driver capability" unfortunately broke iwrange -- it used the variable c that needs to be 0 for the channel list. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 226cf86..d39688c 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -154,7 +154,7 @@ int cfg80211_wext_giwrange(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_range *range = (struct iw_range *) extra; enum ieee80211_band band; - int c = 0; + int i, c = 0; if (!wdev) return -EOPNOTSUPP; @@ -201,8 +201,8 @@ int cfg80211_wext_giwrange(struct net_device *dev, range->avg_qual.noise = range->max_qual.noise / 2; range->avg_qual.updated = range->max_qual.updated; - for (c = 0; c < wdev->wiphy->n_cipher_suites; c++) { - switch (wdev->wiphy->cipher_suites[c]) { + for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { + switch (wdev->wiphy->cipher_suites[i]) { case WLAN_CIPHER_SUITE_TKIP: range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_WPA); @@ -226,7 +226,6 @@ int cfg80211_wext_giwrange(struct net_device *dev, } for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { - int i; struct ieee80211_supported_band *sband; sband = wdev->wiphy->bands[band]; -- cgit v1.1 From 96f7e73938da744e718ce4817dd3e7e424624654 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Tue, 30 Jun 2009 14:49:18 +0200 Subject: mac80211: shorten the passive dwell time for sw scans mac80211's software scan implementation uses a passive dwell time of (HZ / 5) which means we stay 200ms on each passive channel. Compared to iwlwifi's hw scan and the old ipw* drivers which use values around 120ms this is quite long. Reducing the passive dwell time from 200ms to 125ms should save us something around a second on cards capable of 11a and we should still be able to catch beacons from most access points (assuming a ~100ms beacon interval). Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 8b2416c..569a464 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -26,7 +26,7 @@ #define IEEE80211_PROBE_DELAY (HZ / 33) #define IEEE80211_CHANNEL_TIME (HZ / 33) -#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) +#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) struct ieee80211_bss * ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, -- cgit v1.1 From 6c230c02700f9d2aebe6edf9f571835c06707940 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 3 Jul 2009 02:00:48 +0200 Subject: cfg80211: check for current_bss from giwrate When connecting to an ESSID manually, we may not set the BSSID, and thus wdev->wext.connect.bssid will be NULL. wdev->current_bss is always updated when a connection is established so we should check it first. Signed-off-by: Samuel Ortiz Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index d39688c..2c33cd8 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1136,8 +1136,11 @@ int cfg80211_wext_giwrate(struct net_device *dev, if (!rdev->ops->get_station) return -EOPNOTSUPP; - addr = wdev->wext.connect.bssid; - if (!addr) + if (wdev->current_bss) + addr = wdev->current_bss->pub.bssid; + else if (wdev->wext.connect.bssid) + addr = wdev->wext.connect.bssid; + else return -EOPNOTSUPP; err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); -- cgit v1.1 From 1be491fca12ff599c37ceaf7e9042ebee9f0068e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 5 Jul 2009 14:51:06 +0200 Subject: rfkill: prep for rfkill API changes We've designed the /dev/rfkill API in a way that we can increase the event struct by adding members at the end, should it become necessary. To validate the events, userspace and the kernel need to have the proper event size to check for -- when reading from the other end they need to verify that it's at least version 1 of the event API, with the current struct size, so define a constant for that and make the code a little more 'future proof'. Not that I expect that we'll have to change the event size any time soon, but it's better to write the code in a way that lends itself to extending. Due to the current size of the event struct, the code is currently equivalent, but should the event struct ever need to be increased the new code might not need changing. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/rfkill/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 79693fe..47497c9 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1076,10 +1076,16 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, struct rfkill_event ev; /* we don't need the 'hard' variable but accept it */ - if (count < sizeof(ev) - 1) + if (count < RFKILL_EVENT_SIZE_V1 - 1) return -EINVAL; - if (copy_from_user(&ev, buf, sizeof(ev) - 1)) + /* + * Copy as much data as we can accept into our 'ev' buffer, + * but tell userspace how much we've copied so it can determine + * our API version even in a write() call, if it cares. + */ + count = min(count, sizeof(ev)); + if (copy_from_user(&ev, buf, count)) return -EFAULT; if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL) -- cgit v1.1 From 3e5d7649a64e558e4146ddfad4dfcf13fc65dd47 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 14:37:26 +0200 Subject: cfg80211: let SME control reassociation vs. association Since we don't really know that well in the kernel, let's let the SME control whether it wants to use reassociation or not, by allowing it to give the previous BSSID in the associate() parameters. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 6 ++++++ net/mac80211/mlme.c | 7 ------- net/wireless/core.h | 3 ++- net/wireless/mlme.c | 4 +++- net/wireless/nl80211.c | 10 +++++++--- net/wireless/sme.c | 8 +++++++- 6 files changed, 25 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0f29cd0..e6d8860 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1256,6 +1256,12 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; } + if (req->prev_bssid) { + sdata->u.mgd.flags |= IEEE80211_STA_PREV_BSSID_SET; + memcpy(sdata->u.mgd.prev_bssid, req->prev_bssid, ETH_ALEN); + } else + sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET; + if (req->crypto.control_port) sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; else diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aa1829a..2448645 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -879,9 +879,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_put(local, bss); } - ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET; - memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN); - ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); @@ -1470,10 +1467,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", sdata->dev->name, status_code); - /* if this was a reassociation, ensure we try a "full" - * association next time. This works around some broken APs - * which do not correctly reject reassociation requests. */ - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); /* Wait for SME to decide what to do next */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 82918f5..4554453 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -202,7 +202,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 020f33b..087d337 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -335,7 +335,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *ssid, int ssid_len, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt) { @@ -353,6 +354,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, req.ie_len = ie_len; memcpy(&req.crypto, crypt, sizeof(req.crypto)); req.use_mfp = use_mfp; + req.prev_bssid = prev_bssid; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!req.bss) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 723512b..44c520c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -71,6 +71,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, @@ -3187,7 +3188,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_crypto_settings crypto; struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL; + const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; @@ -3248,10 +3249,13 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_PREV_BSSID]) + prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); + err = nl80211_crypto_settings(info, &crypto, 1); if (!err) - err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, - ssid_len, ie, ie_len, use_mfp, + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, &crypto); out: diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 412161f..066a19e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -125,8 +125,14 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!drv->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; + /* + * We could, later, implement roaming here and then actually + * set prev_bssid to non-NULL. But then we need to be aware + * that some APs don't like that -- so we'd need to retry + * the association. + */ err = cfg80211_mlme_assoc(drv, wdev->netdev, - params->channel, params->bssid, + params->channel, params->bssid, NULL, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); -- cgit v1.1 From a7c1cfc9616ee76213a6d4fd4c17f13fdc92ddce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:45:16 +0200 Subject: mac80211: remove dead code from mlme The ap_capab and last_probe struct members are unused. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/debugfs_netdev.c | 3 --- net/mac80211/ieee80211_i.h | 4 +--- net/mac80211/mlme.c | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index ffe9add..4c541f0 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -100,7 +100,6 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC); IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE); IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); -IEEE80211_IF_FILE(ap_capab, u.mgd.ap_capab, HEX); IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE); IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC); @@ -186,7 +185,6 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(prev_bssid, sta); DEBUGFS_ADD(ssid_len, sta); DEBUGFS_ADD(aid, sta); - DEBUGFS_ADD(ap_capab, sta); DEBUGFS_ADD(capab, sta); DEBUGFS_ADD(extra_ie_len, sta); DEBUGFS_ADD(auth_tries, sta); @@ -318,7 +316,6 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_DEL(prev_bssid, sta); DEBUGFS_DEL(ssid_len, sta); DEBUGFS_DEL(aid, sta); - DEBUGFS_DEL(ap_capab, sta); DEBUGFS_DEL(capab, sta); DEBUGFS_DEL(extra_ie_len, sta); DEBUGFS_DEL(auth_tries, sta); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a550aeb..d0354b1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -268,7 +268,7 @@ struct ieee80211_if_managed { } state; u16 aid; - u16 ap_capab, capab; + u16 capab; u8 *extra_ie; /* to be added to the end of AssocReq */ size_t extra_ie_len; @@ -288,7 +288,6 @@ struct ieee80211_if_managed { unsigned long request; - unsigned long last_probe; unsigned long last_beacon; unsigned int flags; @@ -472,7 +471,6 @@ struct ieee80211_sub_if_data { struct dentry *prev_bssid; struct dentry *ssid_len; struct dentry *aid; - struct dentry *ap_capab; struct dentry *capab; struct dentry *extra_ie_len; struct dentry *auth_tries; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2448645..29575ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -879,7 +879,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_put(local, bss); } - ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); sdata->vif.bss_conf.assoc = 1; @@ -1488,7 +1487,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: associated\n", sdata->dev->name); ifmgd->aid = aid; - ifmgd->ap_capab = capab_info; kfree(ifmgd->assocresp_ies); ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt); -- cgit v1.1 From 77fdaa12cea26c204cc12c312fe40bc0f3dcdfd8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:45:17 +0200 Subject: mac80211: rework MLME for multiple authentications Sit tight. This shakes up the world as you know it. Let go of your spaghetti tongs, they will no longer be required, the horrible statemachine in net/mac80211/mlme.c is no more... With the cfg80211 SME mac80211 now has much less to keep track of, but, on the other hand, for FT it needs to be able to keep track of at least one authentication being in progress while associated. So convert from a single state machine to having small ones for all the different things we need to do. For real FT it will still need work wrt. PS, but this should be a good step. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 105 +--- net/mac80211/debugfs_netdev.c | 38 -- net/mac80211/ieee80211_i.h | 86 ++- net/mac80211/iface.c | 28 +- net/mac80211/mlme.c | 1332 ++++++++++++++++++++++------------------- net/mac80211/rx.c | 29 +- net/mac80211/scan.c | 23 +- 7 files changed, 750 insertions(+), 891 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e6d8860..7cfc14e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1172,122 +1172,25 @@ static int ieee80211_scan(struct wiphy *wiphy, static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { - struct ieee80211_sub_if_data *sdata; - const u8 *ssid; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (req->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - sdata->u.mgd.auth_alg = WLAN_AUTH_OPEN; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - sdata->u.mgd.auth_alg = WLAN_AUTH_SHARED_KEY; - break; - case NL80211_AUTHTYPE_FT: - sdata->u.mgd.auth_alg = WLAN_AUTH_FT; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - sdata->u.mgd.auth_alg = WLAN_AUTH_LEAP; - break; - default: - return -EOPNOTSUPP; - } - - memcpy(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN); - - sdata->local->oper_channel = req->bss->channel; - ieee80211_hw_config(sdata->local, 0); - - ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); - if (!ssid) - return -EINVAL; - sdata->u.mgd.ssid_len = *(ssid + 1); - memcpy(sdata->u.mgd.ssid, ssid + 2, sdata->u.mgd.ssid_len); - - 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.state = IEEE80211_STA_MLME_DIRECT_PROBE; - ieee80211_sta_req_auth(sdata); - return 0; + return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { - struct ieee80211_sub_if_data *sdata; - int ret, i; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (memcmp(sdata->u.mgd.bssid, req->bss->bssid, ETH_ALEN) != 0 || - !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) - return -ENOLINK; /* not authenticated */ - - sdata->u.mgd.flags &= ~IEEE80211_STA_DISABLE_11N; - - for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) - if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || - req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || - req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) - sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_11N; - - sdata->local->oper_channel = req->bss->channel; - ieee80211_hw_config(sdata->local, 0); - - ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); - if (ret && ret != -EALREADY) - return ret; - - if (req->use_mfp) { - sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; - sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED; - } else { - sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; - sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; - } - - if (req->prev_bssid) { - sdata->u.mgd.flags |= IEEE80211_STA_PREV_BSSID_SET; - memcpy(sdata->u.mgd.prev_bssid, req->prev_bssid, ETH_ALEN); - } else - sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET; - - if (req->crypto.control_port) - sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - - sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; - ieee80211_sta_req_auth(sdata); - return 0; + return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_deauth_request *req) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - /* TODO: req->ie, req->peer_addr */ - return ieee80211_sta_deauthenticate(sdata, req->reason_code); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - /* TODO: req->ie, req->peer_addr */ - return ieee80211_sta_disassociate(sdata, req->reason_code); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 4c541f0..e9ec6ca 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -95,29 +95,9 @@ IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC); IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC); /* STA attributes */ -IEEE80211_IF_FILE(state, u.mgd.state, DEC); IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); -IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC); -IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE); IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); -IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE); -IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC); -IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC); -IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC); -IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC); - -static ssize_t ieee80211_if_fmt_flags( - const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ - return scnprintf(buf, buflen, "%s%s%s%s%s\n", - sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "", - sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : ""); -} -__IEEE80211_IF_FILE(flags); /* AP attributes */ IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); @@ -180,18 +160,9 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(force_unicast_rateidx, sta); DEBUGFS_ADD(max_ratectrl_rateidx, sta); - DEBUGFS_ADD(state, sta); DEBUGFS_ADD(bssid, sta); - DEBUGFS_ADD(prev_bssid, sta); - DEBUGFS_ADD(ssid_len, sta); DEBUGFS_ADD(aid, sta); DEBUGFS_ADD(capab, sta); - DEBUGFS_ADD(extra_ie_len, sta); - DEBUGFS_ADD(auth_tries, sta); - DEBUGFS_ADD(assoc_tries, sta); - DEBUGFS_ADD(auth_alg, sta); - DEBUGFS_ADD(auth_transaction, sta); - DEBUGFS_ADD(flags, sta); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) @@ -311,18 +282,9 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_DEL(force_unicast_rateidx, sta); DEBUGFS_DEL(max_ratectrl_rateidx, sta); - DEBUGFS_DEL(state, sta); DEBUGFS_DEL(bssid, sta); - DEBUGFS_DEL(prev_bssid, sta); - DEBUGFS_DEL(ssid_len, sta); DEBUGFS_DEL(aid, sta); DEBUGFS_DEL(capab, sta); - DEBUGFS_DEL(extra_ie_len, sta); - DEBUGFS_DEL(auth_tries, sta); - DEBUGFS_DEL(assoc_tries, sta); - DEBUGFS_DEL(auth_alg, sta); - DEBUGFS_DEL(auth_transaction, sta); - DEBUGFS_DEL(flags, sta); } static void del_ap_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d0354b1..2e92bbd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -227,11 +227,32 @@ struct mesh_preq_queue { u8 flags; }; +enum ieee80211_mgd_state { + IEEE80211_MGD_STATE_IDLE, + IEEE80211_MGD_STATE_PROBE, + IEEE80211_MGD_STATE_AUTH, + IEEE80211_MGD_STATE_ASSOC, +}; + +struct ieee80211_mgd_work { + struct list_head list; + struct ieee80211_bss *bss; + int ie_len; + u8 prev_bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + unsigned long timeout; + enum ieee80211_mgd_state state; + u16 auth_alg, auth_transaction; + + int tries; + + /* must be last */ + u8 ie[0]; /* for auth or assoc frame, not probe */ +}; + /* flags used in struct ieee80211_if_managed.flags */ enum ieee80211_sta_flags { - IEEE80211_STA_PREV_BSSID_SET = BIT(0), - IEEE80211_STA_AUTHENTICATED = BIT(1), - IEEE80211_STA_ASSOCIATED = BIT(2), IEEE80211_STA_PROBEREQ_POLL = BIT(3), IEEE80211_STA_CONTROL_PORT = BIT(4), IEEE80211_STA_WMM_ENABLED = BIT(5), @@ -243,8 +264,6 @@ enum ieee80211_sta_flags { /* flags for MLME request */ enum ieee80211_sta_request { IEEE80211_STA_REQ_SCAN, - IEEE80211_STA_REQ_AUTH, - IEEE80211_STA_REQ_RUN, }; struct ieee80211_if_managed { @@ -254,35 +273,17 @@ struct ieee80211_if_managed { struct work_struct chswitch_work; struct work_struct beacon_loss_work; - u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; + struct mutex mtx; + struct ieee80211_bss *associated; + struct list_head work_list; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - size_t ssid_len; - - enum { - IEEE80211_STA_MLME_DISABLED, - IEEE80211_STA_MLME_DIRECT_PROBE, - IEEE80211_STA_MLME_AUTHENTICATE, - IEEE80211_STA_MLME_ASSOCIATE, - IEEE80211_STA_MLME_ASSOCIATED, - } state; + u8 bssid[ETH_ALEN]; u16 aid; u16 capab; - u8 *extra_ie; /* to be added to the end of AssocReq */ - size_t extra_ie_len; - - /* The last AssocReq/Resp IEs */ - u8 *assocreq_ies, *assocresp_ies; - size_t assocreq_ies_len, assocresp_ies_len; struct sk_buff_head skb_queue; - int assoc_scan_tries; /* number of scans done pre-association */ - int direct_probe_tries; /* retries for direct probes */ - int auth_tries; /* retries for auth req */ - int assoc_tries; /* retries for assoc req */ - unsigned long timers_running; /* used for quiesce/restart */ bool powersave; /* powersave requested for this iface */ @@ -292,9 +293,6 @@ struct ieee80211_if_managed { unsigned int flags; - int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ - int auth_transaction; - u32 beacon_crc; enum { @@ -304,10 +302,6 @@ struct ieee80211_if_managed { } mfp; /* management frame protection */ int wmm_last_param_set; - - /* Extra IE data for management frames */ - u8 *sme_auth_ie; - size_t sme_auth_ie_len; }; enum ieee80211_ibss_request { @@ -466,18 +460,9 @@ struct ieee80211_sub_if_data { union { struct { struct dentry *drop_unencrypted; - struct dentry *state; struct dentry *bssid; - struct dentry *prev_bssid; - struct dentry *ssid_len; struct dentry *aid; struct dentry *capab; - struct dentry *extra_ie_len; - struct dentry *auth_tries; - struct dentry *assoc_tries; - struct dentry *auth_alg; - struct dentry *auth_transaction; - struct dentry *flags; struct dentry *force_unicast_rateidx; struct dentry *max_ratectrl_rateidx; } sta; @@ -928,11 +913,16 @@ extern const struct iw_handler_def ieee80211_iw_handler_def; /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); +int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_auth_request *req); +int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_assoc_request *req); +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_deauth_request *req); +int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_disassoc_request *req); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata); -int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason); -int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); @@ -966,8 +956,6 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, void ieee80211_scan_cancel(struct ieee80211_local *local); ieee80211_rx_result ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - const char *ie, size_t len); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); struct ieee80211_bss * @@ -983,8 +971,6 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, u8 *ssid, u8 ssid_len); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); -void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid, - int freq, u8 *ssid, u8 ssid_len); /* interface handling */ int ieee80211_if_add(struct ieee80211_local *local, const char *name, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b87bf42..4839a2d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -233,9 +233,6 @@ static int ieee80211_open(struct net_device *dev) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); break; - case NL80211_IFTYPE_STATION: - sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET; - /* fall through */ default: conf.vif = &sdata->vif; conf.type = sdata->vif.type; @@ -366,18 +363,6 @@ static int ieee80211_stop(struct net_device *dev) rcu_read_unlock(); /* - * Announce that we are leaving the network, in case we are a - * station interface type. This must be done before removing - * all stations associated with sta_info_flush, otherwise STA - * information will be gone and no announce being done. - */ - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.state != IEEE80211_STA_MLME_DISABLED) - ieee80211_sta_deauthenticate(sdata, - WLAN_REASON_DEAUTH_LEAVING); - } - - /* * Remove all stations associated with this interface. * * This must be done before calling ops->remove_interface() @@ -462,7 +447,6 @@ static int ieee80211_stop(struct net_device *dev) netif_addr_unlock_bh(local->mdev); break; case NL80211_IFTYPE_STATION: - memset(sdata->u.mgd.bssid, 0, ETH_ALEN); del_timer_sync(&sdata->u.mgd.chswitch_timer); del_timer_sync(&sdata->u.mgd.timer); /* @@ -485,10 +469,6 @@ static int ieee80211_stop(struct net_device *dev) */ synchronize_rcu(); skb_queue_purge(&sdata->u.mgd.skb_queue); - - kfree(sdata->u.mgd.extra_ie); - sdata->u.mgd.extra_ie = NULL; - sdata->u.mgd.extra_ie_len = 0; /* fall through */ case NL80211_IFTYPE_ADHOC: if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { @@ -650,11 +630,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree_skb(sdata->u.ibss.presp); break; case NL80211_IFTYPE_STATION: - kfree(sdata->u.mgd.extra_ie); - kfree(sdata->u.mgd.assocreq_ies); - kfree(sdata->u.mgd.assocresp_ies); - kfree(sdata->u.mgd.sme_auth_ie); - break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: @@ -937,7 +912,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) continue; /* do not count disabled managed interfaces */ if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.state == IEEE80211_STA_MLME_DISABLED) + !sdata->u.mgd.associated && + list_empty(&sdata->u.mgd.work_list)) continue; /* do not count unused IBSS interfaces */ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 29575ee..108e8c9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -27,20 +27,51 @@ #include "rate.h" #include "led.h" -#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2 #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 #define IEEE80211_MONITORING_INTERVAL (2 * HZ) #define IEEE80211_PROBE_WAIT (HZ / 5) -#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) -#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 +/* + * All cfg80211 functions have to be called outside a locked + * section so that they can acquire a lock themselves... This + * is much simpler than queuing up things in cfg80211, but we + * do need some indirection for that here. + */ +enum rx_mgmt_action { + /* no action required */ + RX_MGMT_NONE, + + /* caller must call cfg80211_send_rx_auth() */ + RX_MGMT_CFG80211_AUTH, + + /* caller must call cfg80211_send_rx_assoc() */ + RX_MGMT_CFG80211_ASSOC, + + /* caller must call cfg80211_send_deauth() */ + RX_MGMT_CFG80211_DEAUTH, + + /* caller must call cfg80211_send_disassoc() */ + RX_MGMT_CFG80211_DISASSOC, + + /* caller must call cfg80211_auth_timeout() & free work */ + RX_MGMT_CFG80211_AUTH_TO, + + /* caller must call cfg80211_assoc_timeout() & free work */ + RX_MGMT_CFG80211_ASSOC_TO, +}; + /* utils */ +static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) +{ + WARN_ON(!mutex_is_locked(&ifmgd->mtx)); +} + static int ecw2cw(int ecw) { return (1 << ecw) - 1; @@ -74,11 +105,10 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, */ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_ht_info *hti, - u16 ap_ht_cap_flags) + const u8 *bssid, u16 ap_ht_cap_flags) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sta_info *sta; u32 changed = 0; u16 ht_opmode; @@ -127,12 +157,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, ieee80211_hw_config(local, 0); rcu_read_lock(); - - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (sta) rate_control_rate_update(local, sband, sta, IEEE80211_RC_HT_CHANGED); - rcu_read_unlock(); } @@ -155,7 +183,8 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, /* frame sending functions */ -static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) +static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -165,14 +194,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) const u8 *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; skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 200 + ifmgd->extra_ie_len + - ifmgd->ssid_len); + sizeof(*mgmt) + 200 + wk->ie_len + + wk->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -191,45 +219,35 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; } - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY) - capab |= WLAN_CAPABILITY_PRIVACY; - if (bss->wmm_used) - wmm = 1; + if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + if (wk->bss->wmm_used) + wmm = 1; - /* get all rates supported by the device and the AP as - * some APs don't like getting a superset of their rates - * in the association request (e.g. D-Link DAP 1353 in - * b-only mode) */ - rates_len = ieee80211_compatible_rates(bss, sband, &rates); + /* get all rates supported by the device and the AP as + * some APs don't like getting a superset of their rates + * in the association request (e.g. D-Link DAP 1353 in + * b-only mode) */ + rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates); - if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && - (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) - capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - - ieee80211_rx_bss_put(local, bss); - } else { - rates = ~0; - rates_len = sband->n_bitrates; - } + if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && + (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); - memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN); - if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { + if (!is_zero_ether_addr(wk->prev_bssid)) { skb_put(skb, 10); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); mgmt->u.reassoc_req.listen_interval = cpu_to_le16(local->hw.conf.listen_interval); - memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid, + memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid, ETH_ALEN); } else { skb_put(skb, 4); @@ -241,10 +259,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } /* SSID */ - ies = pos = skb_put(skb, 2 + ifmgd->ssid_len); + ies = pos = skb_put(skb, 2 + wk->ssid_len); *pos++ = WLAN_EID_SSID; - *pos++ = ifmgd->ssid_len; - memcpy(pos, ifmgd->ssid, ifmgd->ssid_len); + *pos++ = wk->ssid_len; + memcpy(pos, wk->ssid, wk->ssid_len); /* add all rates which were marked to be used above */ supp_rates_len = rates_len; @@ -299,9 +317,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } } - if (ifmgd->extra_ie) { - pos = skb_put(skb, ifmgd->extra_ie_len); - memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len); + if (wk->ie_len && wk->ie) { + pos = skb_put(skb, wk->ie_len); + memcpy(pos, wk->ie, wk->ie_len); } if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) { @@ -326,7 +344,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) */ if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && sband->ht_cap.ht_supported && - (ht_ie = ieee80211_bss_get_ie(&bss->cbss, WLAN_EID_HT_INFORMATION)) && + (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) && ht_ie[1] >= sizeof(struct ieee80211_ht_info) && (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) { struct ieee80211_ht_info *ht_info = @@ -363,18 +381,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } - kfree(ifmgd->assocreq_ies); - ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies; - ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL); - if (ifmgd->assocreq_ies) - memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len); - ieee80211_tx_skb(sdata, skb, 0); } static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - u16 stype, u16 reason) + const u8 *bssid, u16 stype, u16 reason) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -391,9 +403,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); - memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); skb_put(skb, 2); /* u.deauth.reason_code == u.disassoc.reason_code */ @@ -477,28 +489,26 @@ static void ieee80211_chswitch_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); - struct ieee80211_bss *bss; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (!netif_running(sdata->dev)) return; - bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (!bss) - goto exit; + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) + goto out; sdata->local->oper_channel = sdata->local->csa_channel; + ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); + /* XXX: shouldn't really modify cfg80211-owned data! */ - if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) - bss->cbss.channel = sdata->local->oper_channel; + ifmgd->associated->cbss.channel = sdata->local->oper_channel; - ieee80211_rx_bss_put(sdata->local, bss); -exit: - ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); + out: + ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + mutex_unlock(&ifmgd->mtx); } static void ieee80211_chswitch_timer(unsigned long data) @@ -523,7 +533,9 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); - if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED) + ASSERT_MGD_MTX(ifmgd); + + if (!ifmgd->associated) return; if (sdata->local->sw_scanning || sdata->local->hw_scanning) @@ -634,7 +646,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) } if (count == 1 && found->u.mgd.powersave && - (found->u.mgd.flags & IEEE80211_STA_ASSOCIATED) && + found->u.mgd.associated && list_empty(&found->u.mgd.work_list) && !(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) { s32 beaconint_us; @@ -789,9 +801,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, u16 capab, bool erp_valid, u8 erp) { struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -#endif u32 changed = 0; bool use_protection; bool use_short_preamble; @@ -808,42 +817,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); if (use_protection != bss_conf->use_cts_prot) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n", - sdata->dev->name, - use_protection ? "enabled" : "disabled", - ifmgd->bssid); - } -#endif bss_conf->use_cts_prot = use_protection; changed |= BSS_CHANGED_ERP_CTS_PROT; } if (use_short_preamble != bss_conf->use_short_preamble) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: switched to %s barker preamble" - " (BSSID=%pM)\n", - sdata->dev->name, - use_short_preamble ? "short" : "long", - ifmgd->bssid); - } -#endif bss_conf->use_short_preamble = use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } if (use_short_slot != bss_conf->use_short_slot) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: switched to %s slot time" - " (BSSID=%pM)\n", - sdata->dev->name, - use_short_slot ? "short" : "long", - ifmgd->bssid); - } -#endif bss_conf->use_short_slot = use_short_slot; changed |= BSS_CHANGED_ERP_SLOT; } @@ -852,32 +835,23 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, + struct ieee80211_bss *bss, u32 bss_info_changed) { - 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; bss_info_changed |= BSS_CHANGED_ASSOC; - ifmgd->flags |= IEEE80211_STA_ASSOCIATED; + /* set timing information */ + sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval; + sdata->vif.bss_conf.timestamp = bss->cbss.tsf; + sdata->vif.bss_conf.dtim_period = bss->dtim_period; - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - conf->channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - /* set timing information */ - sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval; - sdata->vif.bss_conf.timestamp = bss->cbss.tsf; - sdata->vif.bss_conf.dtim_period = bss->dtim_period; + bss_info_changed |= BSS_CHANGED_BEACON_INT; + bss_info_changed |= ieee80211_handle_bss_capability(sdata, + bss->cbss.capability, bss->has_erp_value, bss->erp_value); - bss_info_changed |= BSS_CHANGED_BEACON_INT; - bss_info_changed |= ieee80211_handle_bss_capability(sdata, - bss->cbss.capability, bss->has_erp_value, bss->erp_value); - - ieee80211_rx_bss_put(local, bss); - } + sdata->u.mgd.associated = bss; + memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN); ieee80211_led_assoc(local, 1); @@ -905,152 +879,133 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, netif_carrier_on(sdata->dev); } -static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - ifmgd->direct_probe_tries++; - if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid, - GFP_KERNEL); + sdata->dev->name, wk->bss->cbss.bssid); /* * Most likely AP is not in the range so remove the - * bss information associated to the AP + * bss struct for that AP. */ - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_DIRECT_PROBE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_AUTH_TO; } - printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n", - sdata->dev->name, ifmgd->bssid, - ifmgd->direct_probe_tries); + printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, + wk->tries); - ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; - - /* Direct probe is sent to broadcast address as some APs + /* + * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. */ - ieee80211_send_probe_req(sdata, NULL, - ifmgd->ssid, ifmgd->ssid_len, NULL, 0); + ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0); + + wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); + return RX_MGMT_NONE; } -static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - ifmgd->auth_tries++; - if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: authentication with AP %pM" " timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid, - GFP_KERNEL); - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + sdata->dev->name, wk->bss->cbss.bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_AUTHENTICATE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_AUTH_TO; } - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - printk(KERN_DEBUG "%s: authenticate with AP %pM\n", - sdata->dev->name, ifmgd->bssid); + printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, wk->tries); + + ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len, + wk->bss->cbss.bssid, 0); + wk->auth_transaction = 2; - ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ifmgd->sme_auth_ie, - ifmgd->sme_auth_ie_len, ifmgd->bssid, 0); - ifmgd->auth_transaction = 2; + wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); + return RX_MGMT_NONE; } -/* - * The disassoc 'reason' argument can be either our own reason - * if self disconnected or a reason code from the AP. - */ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, - bool deauth, bool self_disconnected, - u16 reason) + const u8 *bssid, bool deauth) { 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; - if (deauth) { - ifmgd->direct_probe_tries = 0; - ifmgd->auth_tries = 0; - } - ifmgd->assoc_scan_tries = 0; - ifmgd->assoc_tries = 0; + ASSERT_MGD_MTX(ifmgd); + + ifmgd->associated = NULL; + memset(ifmgd->bssid, 0, ETH_ALEN); + + /* + * we need to commit the associated = NULL change because the + * scan code uses that to determine whether this iface should + * go to/wake up from powersave or not -- and could otherwise + * wake the queues erroneously. + */ + smp_mb(); + + /* + * Thus, we can only afterwards stop the queues -- to account + * for the case where another CPU is finishing a scan at this + * time -- we don't want the scan code to enable queues. + */ netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (sta) ieee80211_sta_tear_down_BA_sessions(sta); rcu_read_unlock(); - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - conf->channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - - if (bss) - ieee80211_rx_bss_put(local, bss); - - if (self_disconnected) { - if (deauth) - ieee80211_send_deauth_disassoc(sdata, - IEEE80211_STYPE_DEAUTH, reason); - else - ieee80211_send_deauth_disassoc(sdata, - IEEE80211_STYPE_DISASSOC, reason); - } - - ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED; changed |= ieee80211_reset_erp_info(sdata); ieee80211_led_assoc(local, 0); changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; - if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) { - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - } - ieee80211_set_wmm_default(sdata); ieee80211_recalc_idle(local); @@ -1079,7 +1034,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (!sta) { rcu_read_unlock(); return; @@ -1092,38 +1047,42 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sta_info_destroy(sta); } -static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_associate(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - ifmgd->assoc_tries++; - if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with AP %pM" " timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid, - GFP_KERNEL); - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + sdata->dev->name, wk->bss->cbss.bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); + /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_ASSOCIATE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_ASSOC_TO; } - ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; - printk(KERN_DEBUG "%s: associate with AP %pM\n", - sdata->dev->name, ifmgd->bssid); - ieee80211_send_assoc(sdata); + printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, wk->tries); + ieee80211_send_assoc(sdata, wk); + + wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); + return RX_MGMT_NONE; } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -1148,6 +1107,7 @@ void ieee80211_beacon_loss_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.beacon_loss_work); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ssid; /* * The driver has already reported this event and we have @@ -1160,12 +1120,15 @@ void ieee80211_beacon_loss_work(struct work_struct *work) if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) return; + mutex_lock(&ifmgd->mtx); + + if (!ifmgd->associated) + goto out; + #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " - "- sending probe request\n", sdata->dev->name, - sdata->u.mgd.bssid); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: driver reports beacon loss from AP " + "- sending probe request\n", sdata->dev->name); #endif ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; @@ -1174,10 +1137,13 @@ void ieee80211_beacon_loss_work(struct work_struct *work) ieee80211_recalc_ps(sdata->local, -1); mutex_unlock(&sdata->local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); + ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID); + ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, + ssid + 2, ssid[1], NULL, 0); mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT); + out: + mutex_unlock(&ifmgd->mtx); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) @@ -1189,102 +1155,16 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_beacon_loss); -static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) +static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct sta_info *sta; - unsigned long last_rx; - bool disassoc = false; - - /* TODO: start monitoring current AP signal quality and number of - * missed beacons. Scan other channels every now and then and search - * for better APs. */ - /* TODO: remove expired BSSes */ - - ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED; - - rcu_read_lock(); - - sta = sta_info_get(local, ifmgd->bssid); - if (!sta) { - printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n", - sdata->dev->name, ifmgd->bssid); - disassoc = true; - rcu_read_unlock(); - goto out; - } - - last_rx = sta->last_rx; - rcu_read_unlock(); - - if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) && - time_after(jiffies, last_rx + IEEE80211_PROBE_WAIT)) { - printk(KERN_DEBUG "%s: no probe response from AP %pM " - "- disassociating\n", - sdata->dev->name, ifmgd->bssid); - disassoc = true; - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; - goto out; - } - - /* - * 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)) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: beacon loss from AP %pM " - "- sending probe request\n", - sdata->dev->name, ifmgd->bssid); - } -#endif - ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT); - goto out; - } - - if (time_after(jiffies, last_rx + IEEE80211_PROBE_IDLE_TIME)) { - ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); - } - - out: - if (!disassoc) - mod_timer(&ifmgd->timer, - jiffies + IEEE80211_MONITORING_INTERVAL); - else - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_PREV_AUTH_NOT_VALID); -} - - -static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - + wk->state = IEEE80211_MGD_STATE_IDLE; printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name); - ifmgd->flags |= IEEE80211_STA_AUTHENTICATED; - /* Wait for SME to request association */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(sdata->local); } static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, struct ieee80211_mgmt *mgmt, size_t len) { @@ -1295,120 +1175,132 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (!elems.challenge) return; - ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg, + ieee80211_send_auth(sdata, 3, wk->auth_alg, elems.challenge - 2, elems.challenge_len + 2, - sdata->u.mgd.bssid, 1); - sdata->u.mgd.auth_transaction = 4; + wk->bss->cbss.bssid, 1); + wk->auth_transaction = 4; } -static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 auth_alg, auth_transaction, status_code; - if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE) - return; + if (wk->state != IEEE80211_MGD_STATE_AUTH) + return RX_MGMT_NONE; if (len < 24 + 6) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0) + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0) + return RX_MGMT_NONE; auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); status_code = le16_to_cpu(mgmt->u.auth.status_code); - if (auth_alg != ifmgd->auth_alg || - auth_transaction != ifmgd->auth_transaction) - return; + if (auth_alg != wk->auth_alg || + auth_transaction != wk->auth_transaction) + return RX_MGMT_NONE; if (status_code != WLAN_STATUS_SUCCESS) { - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, - GFP_KERNEL); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(sdata->local); - return; + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_AUTH; } - switch (ifmgd->auth_alg) { + switch (wk->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, - GFP_KERNEL); - break; + ieee80211_auth_completed(sdata, wk); + return RX_MGMT_CFG80211_AUTH; case WLAN_AUTH_SHARED_KEY: - if (ifmgd->auth_transaction == 4) { - ieee80211_auth_completed(sdata); - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len, - GFP_KERNEL); + if (wk->auth_transaction == 4) { + ieee80211_auth_completed(sdata, wk); + return RX_MGMT_CFG80211_AUTH; } else - ieee80211_auth_challenge(sdata, mgmt, len); + ieee80211_auth_challenge(sdata, wk, mgmt, len); break; } + + return RX_MGMT_NONE; } -static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *bssid = NULL; u16 reason_code; if (len < 24 + 2) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN)) - return; + ASSERT_MGD_MTX(ifmgd); + + if (wk) + bssid = wk->bss->cbss.bssid; + else + bssid = ifmgd->associated->cbss.bssid; reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED) - printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n", - sdata->dev->name, reason_code); + printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", + sdata->dev->name, bssid, reason_code); + + if (!wk) { + ieee80211_set_disassoc(sdata, bssid, true); + } else { + list_del(&wk->list); + kfree(wk); + } - ieee80211_set_disassoc(sdata, true, false, 0); - ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); + return RX_MGMT_CFG80211_DEAUTH; } -static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; if (len < 24 + 2) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN)) - return; + ASSERT_MGD_MTX(ifmgd); + + if (WARN_ON(!ifmgd->associated)) + return RX_MGMT_NONE; + + if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN))) + return RX_MGMT_NONE; reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - if (ifmgd->flags & IEEE80211_STA_ASSOCIATED) - printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", - sdata->dev->name, reason_code); + printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", + sdata->dev->name, reason_code); - ieee80211_set_disassoc(sdata, false, false, reason_code); - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); + ieee80211_set_disassoc(sdata, ifmgd->associated->cbss.bssid, false); + return RX_MGMT_CFG80211_DISASSOC; } -static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - int reassoc) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len, + bool reassoc) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -1424,17 +1316,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bool have_higher_than_11mbit = false, newsta = false; u16 ap_ht_cap_flags; - /* AssocResp and ReassocResp have identical structure, so process both - * of them in this function. */ - - if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) - return; + /* + * AssocResp and ReassocResp have identical structure, so process both + * of them in this function. + */ if (len < 24 + 6) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0) + return RX_MGMT_NONE; capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -1457,21 +1348,19 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: AP rejected association temporarily; " "comeback duration %u TU (%u ms)\n", sdata->dev->name, tu, ms); + wk->timeout = jiffies + msecs_to_jiffies(ms); if (ms > IEEE80211_ASSOC_TIMEOUT) mod_timer(&ifmgd->timer, jiffies + msecs_to_jiffies(ms)); - return; + return RX_MGMT_NONE; } if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", sdata->dev->name, status_code); - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, - GFP_KERNEL); - /* Wait for SME to decide what to do next */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - return; + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_ASSOC; } if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) @@ -1482,50 +1371,38 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (!elems.supp_rates) { printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", sdata->dev->name); - return; + return RX_MGMT_NONE; } printk(KERN_DEBUG "%s: associated\n", sdata->dev->name); ifmgd->aid = aid; - kfree(ifmgd->assocresp_ies); - ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt); - ifmgd->assocresp_ies = kmalloc(ifmgd->assocresp_ies_len, GFP_KERNEL); - if (ifmgd->assocresp_ies) - memcpy(ifmgd->assocresp_ies, pos, ifmgd->assocresp_ies_len); - rcu_read_lock(); /* Add STA entry for the AP */ - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, wk->bss->cbss.bssid); if (!sta) { newsta = true; - sta = sta_info_alloc(sdata, ifmgd->bssid, GFP_ATOMIC); + rcu_read_unlock(); + + sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL); if (!sta) { printk(KERN_DEBUG "%s: failed to alloc STA entry for" " the AP\n", sdata->dev->name); - rcu_read_unlock(); - return; + return RX_MGMT_NONE; } /* update new sta with its last rx activity */ sta->last_rx = jiffies; - } - /* - * FIXME: Do we really need to update the sta_info's information here? - * We already know about the AP (we found it in our list) so it - * should already be filled with the right info, no? - * As is stands, all this is racy because typically we assume - * the information that is filled in here (except flags) doesn't - * change while a STA structure is alive. As such, it should move - * to between the sta_info_alloc() and sta_info_insert() above. - */ + set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_AP); + if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) + set_sta_flags(sta, WLAN_STA_AUTHORIZED); - set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP); - if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + rcu_read_lock(); + } rates = 0; basic_rates = 0; @@ -1595,7 +1472,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: failed to insert STA entry for" " the AP (error %d)\n", sdata->dev->name, err); rcu_read_unlock(); - return; + return RX_MGMT_NONE; } } @@ -1611,13 +1488,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, + wk->bss->cbss.bssid, ap_ht_cap_flags); /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; bss_conf->assoc_capability = capab_info; - ieee80211_set_associated(sdata, changed); + ieee80211_set_associated(sdata, wk->bss, changed); /* * initialise the time of last beacon to be the association time, @@ -1625,8 +1503,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, */ ifmgd->last_beacon = jiffies; - ieee80211_associated(sdata); - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL); + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_ASSOC; } @@ -1654,23 +1533,25 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, channel, beacon); - if (!bss) + if (bss) + ieee80211_rx_bss_put(local, bss); + + if (!sdata->u.mgd.associated) return; if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && - (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) { + (memcmp(mgmt->bssid, sdata->u.mgd.associated->cbss.bssid, + ETH_ALEN) == 0)) { struct ieee80211_channel_sw_ie *sw_elem = (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); } - - ieee80211_rx_bss_put(local, bss); } static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd; @@ -1679,6 +1560,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ifmgd = &sdata->u.mgd; + ASSERT_MGD_MTX(ifmgd); + if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ @@ -1692,13 +1575,17 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); /* direct probe may be part of the association flow */ - if (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE) { + if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) { printk(KERN_DEBUG "%s direct probe responded\n", sdata->dev->name); - ieee80211_authenticate(sdata); + wk->tries = 0; + wk->state = IEEE80211_MGD_STATE_AUTH; + WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE); } - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { + if (ifmgd->associated && + memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 && + ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); @@ -1740,6 +1627,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, bool erp_valid, directed_tim = false; u8 erp_value = 0; u32 ncrc; + u8 *bssid; + + ASSERT_MGD_MTX(ifmgd); /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; @@ -1749,8 +1639,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (rx_status->freq != local->hw.conf.channel->center_freq) return; - if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED) || - memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0) + if (WARN_ON(!ifmgd->associated)) + return; + + bssid = ifmgd->associated->cbss.bssid; + + if (WARN_ON(memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)) return; if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { @@ -1829,8 +1723,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); - if (!sta) { + sta = sta_info_get(local, bssid); + if (WARN_ON(!sta)) { rcu_read_unlock(); return; } @@ -1845,7 +1739,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - ap_ht_cap_flags); + bssid, ap_ht_cap_flags); } if (elems.country_elem) { @@ -1887,6 +1781,7 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_REASSOC_RESP: case IEEE80211_STYPE_DEAUTH: case IEEE80211_STYPE_DISASSOC: + case IEEE80211_STYPE_ACTION: skb_queue_tail(&sdata->u.mgd.skb_queue, skb); queue_work(local->hw.workqueue, &sdata->u.mgd.work); return RX_QUEUED; @@ -1898,40 +1793,118 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; + struct ieee80211_mgd_work *wk; + enum rx_mgmt_action rma = RX_MGMT_NONE; u16 fc; rx_status = (struct ieee80211_rx_status *) skb->cb; mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, - rx_status); - break; - case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, - rx_status); - break; - case IEEE80211_STYPE_AUTH: - ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_ASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 0); + mutex_lock(&ifmgd->mtx); + + if (ifmgd->associated && + memcmp(ifmgd->associated->cbss.bssid, mgmt->bssid, + ETH_ALEN) == 0) { + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, + rx_status); + break; + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt, + skb->len, rx_status); + break; + case IEEE80211_STYPE_DEAUTH: + rma = ieee80211_rx_mgmt_deauth(sdata, NULL, + mgmt, skb->len); + break; + case IEEE80211_STYPE_DISASSOC: + rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_ACTION: + /* XXX: differentiate, can only happen for CSA now! */ + ieee80211_sta_process_chanswitch(sdata, + &mgmt->u.action.u.chan_switch.sw_elem, + ifmgd->associated); + break; + } + mutex_unlock(&ifmgd->mtx); + + switch (rma) { + case RX_MGMT_NONE: + /* no action */ + break; + case RX_MGMT_CFG80211_DEAUTH: + cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, + skb->len, GFP_KERNEL); + break; + case RX_MGMT_CFG80211_DISASSOC: + cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, + skb->len, GFP_KERNEL); + break; + default: + WARN(1, "unexpected: %d", rma); + } + goto out; + } + + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0) + continue; + + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len, + rx_status); + break; + case IEEE80211_STYPE_AUTH: + rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len); + break; + case IEEE80211_STYPE_ASSOC_RESP: + rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, + skb->len, false); + break; + case IEEE80211_STYPE_REASSOC_RESP: + rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, + skb->len, true); + break; + case IEEE80211_STYPE_DEAUTH: + rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt, + skb->len); + break; + } + /* + * We've processed this frame for that work, so it can't + * belong to another work struct. + * NB: this is also required for correctness because the + * called functions can free 'wk', and for 'rma'! + */ break; - case IEEE80211_STYPE_REASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 1); + } + + mutex_unlock(&ifmgd->mtx); + + switch (rma) { + case RX_MGMT_NONE: + /* no action */ break; - case IEEE80211_STYPE_DEAUTH: - ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); + case RX_MGMT_CFG80211_AUTH: + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len, + GFP_KERNEL); break; - case IEEE80211_STYPE_DISASSOC: - ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + case RX_MGMT_CFG80211_ASSOC: + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len, + GFP_KERNEL); break; + default: + WARN(1, "unexpected: %d", rma); } + out: kfree_skb(skb); } @@ -1947,89 +1920,9 @@ static void ieee80211_sta_timer(unsigned long data) return; } - set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); queue_work(local->hw.workqueue, &ifmgd->work); } -static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - - /* Reset own TSF to allow time synchronization work. */ - drv_reset_tsf(local); - - ifmgd->wmm_last_param_set = -1; /* allow any WMM update */ - ifmgd->auth_transaction = -1; - ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED; - ifmgd->assoc_scan_tries = 0; - ifmgd->direct_probe_tries = 0; - ifmgd->auth_tries = 0; - ifmgd->assoc_tries = 0; - netif_tx_stop_all_queues(sdata->dev); - netif_carrier_off(sdata->dev); -} - -static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct ieee80211_bss *bss; - u8 *bssid = ifmgd->bssid, *ssid = ifmgd->ssid; - u8 ssid_len = ifmgd->ssid_len; - u16 capa_mask = WLAN_CAPABILITY_ESS; - u16 capa_val = WLAN_CAPABILITY_ESS; - struct ieee80211_channel *chan = local->oper_channel; - - bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, - bssid, ssid, ssid_len, - capa_mask, capa_val); - - if (bss) { - local->oper_channel = bss->cbss.channel; - local->oper_channel_type = NL80211_CHAN_NO_HT; - ieee80211_hw_config(local, 0); - - ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len, - bss->supp_rates); - if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED) - sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; - - /* Send out direct probe if no probe resp was received or - * the one we have is outdated - */ - if (!bss->last_probe_resp || - time_after(jiffies, bss->last_probe_resp - + IEEE80211_SCAN_RESULT_EXPIRE)) - ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; - else - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - - ieee80211_rx_bss_put(local, bss); - ieee80211_sta_reset_auth(sdata); - return 0; - } else { - if (ifmgd->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { - - ifmgd->assoc_scan_tries++; - - ieee80211_request_internal_scan(sdata, ifmgd->ssid, - ssid_len); - - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); - } else { - ifmgd->assoc_scan_tries = 0; - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - } - } - return -1; -} - - static void ieee80211_sta_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = @@ -2037,6 +1930,10 @@ static void ieee80211_sta_work(struct work_struct *work) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd; struct sk_buff *skb; + struct ieee80211_mgd_work *wk, *tmp; + LIST_HEAD(free_work); + enum rx_mgmt_action rma; + bool anybusy = false; if (!netif_running(sdata->dev)) return; @@ -2059,46 +1956,93 @@ static void ieee80211_sta_work(struct work_struct *work) ifmgd = &sdata->u.mgd; + /* first process frames to avoid timing out while a frame is pending */ while ((skb = skb_dequeue(&ifmgd->skb_queue))) ieee80211_sta_rx_queued_mgmt(sdata, skb); - if (ifmgd->state != IEEE80211_STA_MLME_DIRECT_PROBE && - ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE && - ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE && - test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) { - queue_delayed_work(local->hw.workqueue, &local->scan_work, - round_jiffies_relative(0)); - return; + /* then process the rest of the work */ + mutex_lock(&ifmgd->mtx); + + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (wk->state != IEEE80211_MGD_STATE_IDLE) { + anybusy = true; + break; + } } - if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request)) { - if (ieee80211_sta_config_auth(sdata)) - return; - clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); - } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request)) + ieee80211_recalc_idle(local); + + if (!anybusy) { + mutex_unlock(&ifmgd->mtx); + + if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) + queue_delayed_work(local->hw.workqueue, + &local->scan_work, + round_jiffies_relative(0)); return; + } - ieee80211_recalc_idle(local); + list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { + if (time_before(jiffies, wk->timeout)) + continue; - switch (ifmgd->state) { - case IEEE80211_STA_MLME_DISABLED: - break; - case IEEE80211_STA_MLME_DIRECT_PROBE: - ieee80211_direct_probe(sdata); - break; - case IEEE80211_STA_MLME_AUTHENTICATE: - ieee80211_authenticate(sdata); - break; - case IEEE80211_STA_MLME_ASSOCIATE: - ieee80211_associate(sdata); - break; - case IEEE80211_STA_MLME_ASSOCIATED: - ieee80211_associated(sdata); - break; - default: - WARN_ON(1); - break; + switch (wk->state) { + default: + WARN_ON(1); + /* fall through */ + case IEEE80211_MGD_STATE_IDLE: + /* nothing */ + rma = RX_MGMT_NONE; + break; + case IEEE80211_MGD_STATE_PROBE: + rma = ieee80211_direct_probe(sdata, wk); + break; + case IEEE80211_MGD_STATE_AUTH: + rma = ieee80211_authenticate(sdata, wk); + break; + case IEEE80211_MGD_STATE_ASSOC: + rma = ieee80211_associate(sdata, wk); + break; + } + + switch (rma) { + case RX_MGMT_NONE: + /* no action required */ + break; + case RX_MGMT_CFG80211_AUTH_TO: + case RX_MGMT_CFG80211_ASSOC_TO: + list_del(&wk->list); + list_add(&wk->list, &free_work); + wk->tries = rma; /* small abuse but only local */ + break; + default: + WARN(1, "unexpected: %d", rma); + } + } + + mutex_unlock(&ifmgd->mtx); + + list_for_each_entry_safe(wk, tmp, &free_work, list) { + switch (wk->tries) { + case RX_MGMT_CFG80211_AUTH_TO: + cfg80211_send_auth_timeout(sdata->dev, + wk->bss->cbss.bssid, + GFP_KERNEL); + break; + case RX_MGMT_CFG80211_ASSOC_TO: + cfg80211_send_auth_timeout(sdata->dev, + wk->bss->cbss.bssid, + GFP_KERNEL); + break; + default: + WARN(1, "unexpected: %d", wk->tries); + } + + list_del(&wk->list); + kfree(wk); } + + ieee80211_recalc_idle(local); } static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) @@ -2110,7 +2054,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) */ sdata->u.mgd.last_beacon = jiffies; - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); } @@ -2152,7 +2095,6 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd; - u32 hw_flags; ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); @@ -2164,113 +2106,239 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); skb_queue_head_init(&ifmgd->skb_queue); + INIT_LIST_HEAD(&ifmgd->work_list); + ifmgd->capab = WLAN_CAPABILITY_ESS; ifmgd->flags = 0; if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; - hw_flags = sdata->local->hw.flags; + mutex_init(&ifmgd->mtx); } -/* configuration hooks */ -void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) +/* scan finished notification */ +void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *sdata = local->scan_sdata; - if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) - return; + /* Restart STA timers */ + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) + ieee80211_restart_sta_timer(sdata); + rcu_read_unlock(); +} - if (WARN_ON(ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); +int ieee80211_max_network_latency(struct notifier_block *nb, + unsigned long data, void *dummy) +{ + s32 latency_usec = (s32) data; + struct ieee80211_local *local = + container_of(nb, struct ieee80211_local, + network_latency_notifier); - if (WARN_ON(ifmgd->ssid_len == 0)) { - /* - * Only allow association to be started if a valid SSID - * is configured. - */ - return; - } + mutex_lock(&local->iflist_mtx); + ieee80211_recalc_ps(local, latency_usec); + mutex_unlock(&local->iflist_mtx); - set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); - queue_work(local->hw.workqueue, &ifmgd->work); + return 0; } -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - const char *ie, size_t len) +/* config hooks */ +int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_auth_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ssid; + struct ieee80211_mgd_work *wk; + u16 auth_alg; - if (len == 0 && ifmgd->extra_ie_len == 0) - return -EALREADY; - - if (len == ifmgd->extra_ie_len && ifmgd->extra_ie && - memcmp(ifmgd->extra_ie, ie, len) == 0) - return -EALREADY; - - kfree(ifmgd->extra_ie); - if (len == 0) { - ifmgd->extra_ie = NULL; - ifmgd->extra_ie_len = 0; - return 0; + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + auth_alg = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg = WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg = WLAN_AUTH_LEAP; + break; + default: + return -EOPNOTSUPP; } - ifmgd->extra_ie = kmalloc(len, GFP_KERNEL); - if (!ifmgd->extra_ie) { - ifmgd->extra_ie_len = 0; + + wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); + if (!wk) return -ENOMEM; + + wk->bss = (void *)req->bss; + + if (req->ie && req->ie_len) { + memcpy(wk->ie, req->ie, req->ie_len); + wk->ie_len = req->ie_len; } - memcpy(ifmgd->extra_ie, ie, len); - ifmgd->extra_ie_len = len; + + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + memcpy(wk->ssid, ssid + 2, ssid[1]); + wk->ssid_len = ssid[1]; + + wk->state = IEEE80211_MGD_STATE_PROBE; + wk->auth_alg = auth_alg; + + /* + * XXX: if still associated need to tell AP that we're going + * to sleep and then change channel etc. + */ + sdata->local->oper_channel = req->bss->channel; + ieee80211_hw_config(sdata->local, 0); + + mutex_lock(&ifmgd->mtx); + list_add(&wk->list, &sdata->u.mgd.work_list); + mutex_unlock(&ifmgd->mtx); + + queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); return 0; } -int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason) +int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_assoc_request *req) { - printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n", - sdata->dev->name, reason); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_work *wk, *found = NULL; + int i, err; - ieee80211_set_disassoc(sdata, true, true, reason); - return 0; + mutex_lock(&ifmgd->mtx); + + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (&wk->bss->cbss == req->bss && + wk->state == IEEE80211_MGD_STATE_IDLE) { + found = wk; + break; + } + } + + if (!found) { + err = -ENOLINK; + goto out; + } + + list_del(&found->list); + + wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL); + if (!wk) { + list_add(&found->list, &ifmgd->work_list); + err = -ENOMEM; + goto out; + } + + list_add(&wk->list, &ifmgd->work_list); + + ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; + + for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) + if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) + ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + + sdata->local->oper_channel = req->bss->channel; + ieee80211_hw_config(sdata->local, 0); + + if (req->ie && req->ie_len) { + memcpy(wk->ie, req->ie, req->ie_len); + wk->ie_len = req->ie_len; + } else + wk->ie_len = 0; + + if (req->prev_bssid) + memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN); + + wk->state = IEEE80211_MGD_STATE_ASSOC; + wk->tries = 0; + + if (req->use_mfp) { + ifmgd->mfp = IEEE80211_MFP_REQUIRED; + ifmgd->flags |= IEEE80211_STA_MFP_ENABLED; + } else { + ifmgd->mfp = IEEE80211_MFP_DISABLED; + ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; + } + + if (req->crypto.control_port) + ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; + else + ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; + + queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + + err = 0; + + out: + mutex_unlock(&ifmgd->mtx); + return err; } -int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason) +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_deauth_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_work *wk; + const u8 *bssid = NULL; - printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n", - sdata->dev->name, reason); + printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n", + sdata->dev->name, req->reason_code); + + mutex_lock(&ifmgd->mtx); + + if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { + bssid = req->bss->bssid; + ieee80211_set_disassoc(sdata, bssid, true); + } else list_for_each_entry(wk, &ifmgd->work_list, list) { + if (&wk->bss->cbss == req->bss) { + bssid = req->bss->bssid; + list_del(&wk->list); + kfree(wk); + break; + } + } - if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED)) + /* cfg80211 should catch this... */ + if (WARN_ON(!bssid)) { + mutex_unlock(&ifmgd->mtx); return -ENOLINK; + } + + mutex_unlock(&ifmgd->mtx); + + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, req->reason_code); - ieee80211_set_disassoc(sdata, false, true, reason); return 0; } -/* scan finished notification */ -void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) +int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_disassoc_request *req) { - struct ieee80211_sub_if_data *sdata = local->scan_sdata; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - /* Restart STA timers */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) - ieee80211_restart_sta_timer(sdata); - rcu_read_unlock(); -} + printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n", + sdata->dev->name, req->reason_code); -int ieee80211_max_network_latency(struct notifier_block *nb, - unsigned long data, void *dummy) -{ - s32 latency_usec = (s32) data; - struct ieee80211_local *local = - container_of(nb, struct ieee80211_local, - network_latency_notifier); + mutex_lock(&ifmgd->mtx); - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, latency_usec); - mutex_unlock(&local->iflist_mtx); + /* cfg80211 should catch that */ + if (WARN_ON(&ifmgd->associated->cbss != req->bss)) { + mutex_unlock(&ifmgd->mtx); + return -ENOLINK; + } + + ieee80211_set_disassoc(sdata, req->bss->bssid, false); + + mutex_unlock(&ifmgd->mtx); + ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, + IEEE80211_STYPE_DISASSOC, req->reason_code); return 0; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ec5acc6..fe6b990 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1641,12 +1641,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 || compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) { - /* Not from the current AP. */ - return; - } - - if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) { - /* Association in progress; ignore SA Query */ + /* Not from the current AP or not associated yet. */ return; } @@ -1683,7 +1678,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; - struct ieee80211_bss *bss; int len = rx->skb->len; if (!ieee80211_is_action(mgmt->frame_control)) @@ -1761,17 +1755,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) return RX_DROP_MONITOR; - bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid, - local->hw.conf.channel->center_freq, - sdata->u.mgd.ssid, - sdata->u.mgd.ssid_len); - if (!bss) - return RX_DROP_MONITOR; - - ieee80211_sta_process_chanswitch(sdata, - &mgmt->u.action.u.chan_switch.sw_elem, bss); - ieee80211_rx_bss_put(local, bss); - break; + return ieee80211_sta_rx_mgmt(sdata, rx->skb); } break; case WLAN_CATEGORY_SA_QUERY: @@ -2026,13 +2010,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_STATION: if (!bssid) return 0; - if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) { - if (!(rx->flags & IEEE80211_RX_IN_SCAN)) - return 0; - rx->flags &= ~IEEE80211_RX_RA_MATCH; - } else if (!multicast && - compare_ether_addr(sdata->dev->dev_addr, - hdr->addr1) != 0) { + if (!multicast && + compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) { if (!(sdata->dev->flags & IFF_PROMISC)) return 0; rx->flags &= ~IEEE80211_RX_RA_MATCH; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 569a464..5f4f786 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -121,19 +121,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, return bss; } -void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid, - int freq, u8 *ssid, u8 ssid_len) -{ - struct ieee80211_bss *bss; - struct ieee80211_local *local = sdata->local; - - bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len); - if (bss) { - cfg80211_unlink_bss(local->hw.wiphy, (void *)bss); - ieee80211_rx_bss_put(local, bss); - } -} - ieee80211_rx_result ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { @@ -327,7 +314,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) { + if (sdata->u.mgd.associated) { ieee80211_scan_ps_disable(sdata); netif_tx_wake_all_queues(sdata->dev); } @@ -383,7 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) sdata, BSS_CHANGED_BEACON_ENABLED); if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { + if (sdata->u.mgd.associated) { netif_tx_stop_all_queues(sdata->dev); ieee80211_scan_ps_enable(sdata); } @@ -443,10 +430,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (req != &local->int_scan_req && sdata->vif.type == NL80211_IFTYPE_STATION && - (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE || - ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE)) { - /* actually wait for the assoc to finish/time out */ + !list_empty(&ifmgd->work_list)) { + /* actually wait for the work it's doing to finish/time out */ set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request); return 0; } -- cgit v1.1 From c9cf01226e0bd1fa4f7f7024d8d53e982fad208f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:45:18 +0200 Subject: mac80211: refactor the WEP code to be directly usable The new key work for cfg80211 will only give us the WEP key for shared auth to do that authentication, and not via the regular key settings, so we need to be able to encrypt a single frame in software, and that without a key struct. Thus, refactor the WEP code to not require a key structure but use the key, len and idx directly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wep.c | 52 +++++++++++++++++++++++----------------------------- net/mac80211/wep.h | 4 ---- 2 files changed, 23 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index ef73105..4fafb2d 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -67,10 +67,10 @@ static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) static void ieee80211_wep_get_iv(struct ieee80211_local *local, - struct ieee80211_key *key, u8 *iv) + int keylen, int keyidx, u8 *iv) { local->wep_iv++; - if (ieee80211_wep_weak_iv(local->wep_iv, key->conf.keylen)) + if (ieee80211_wep_weak_iv(local->wep_iv, keylen)) local->wep_iv += 0x0100; if (!iv) @@ -79,13 +79,13 @@ static void ieee80211_wep_get_iv(struct ieee80211_local *local, *iv++ = (local->wep_iv >> 16) & 0xff; *iv++ = (local->wep_iv >> 8) & 0xff; *iv++ = local->wep_iv & 0xff; - *iv++ = key->conf.keyidx << 6; + *iv++ = keyidx << 6; } static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) + int keylen, int keyidx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; @@ -100,7 +100,7 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, hdrlen = ieee80211_hdrlen(hdr->frame_control); newhdr = skb_push(skb, WEP_IV_LEN); memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); - ieee80211_wep_get_iv(local, key, newhdr + hdrlen); + ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } @@ -144,26 +144,17 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ -int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) +static int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx) { - u32 klen; - u8 *rc4key, *iv; + u8 *iv; size_t len; + u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; - if (!key || key->conf.alg != ALG_WEP) - return -1; - - klen = 3 + key->conf.keylen; - rc4key = kmalloc(klen, GFP_ATOMIC); - if (!rc4key) - return -1; - - iv = ieee80211_wep_add_iv(local, skb, key); - if (!iv) { - kfree(rc4key); + iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx); + if (!iv) return -1; - } len = skb->len - (iv + WEP_IV_LEN - skb->data); @@ -171,16 +162,14 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, memcpy(rc4key, iv, 3); /* Copy rest of the WEP key (the secret part) */ - memcpy(rc4key + 3, key->conf.key, key->conf.keylen); + memcpy(rc4key + 3, key, keylen); /* Add room for ICV */ skb_put(skb, WEP_ICV_LEN); - ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, klen, + ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3, iv + WEP_IV_LEN, len); - kfree(rc4key); - return 0; } @@ -216,8 +205,9 @@ int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload * is moved to the beginning of the skb and skb length will be reduced. */ -int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) +static int ieee80211_wep_decrypt(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key) { u32 klen; u8 *rc4key; @@ -314,12 +304,16 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { - if (ieee80211_wep_encrypt(tx->local, skb, tx->key)) + if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key, + tx->key->conf.keylen, + tx->key->conf.keyidx)) return -1; } else { info->control.hw_key = &tx->key->conf; if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) { - if (!ieee80211_wep_add_iv(tx->local, skb, tx->key)) + if (!ieee80211_wep_add_iv(tx->local, skb, + tx->key->conf.keylen, + tx->key->conf.keyidx)) return -1; } } diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index d3f0db4..85219de 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -22,10 +22,6 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); -int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key); -int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key); bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); ieee80211_rx_result -- cgit v1.1 From 78485475618cf9f1c70dc7f15e8deafa8b6074ab Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:05 +0200 Subject: cfg80211: fix netdev down problem We shouldn't be looking at the ssid_len for non-IBSS, and for IBSS we should also return an error on trying to leave an IBSS while not in or joining an IBSS. This fixes an issue where we wouldn't disconnect() on an interface being taken down since there's no SSID configured this way. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 3 --- net/wireless/ibss.c | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 7b66cf1..83ec719 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -567,9 +567,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif break; case NETDEV_GOING_DOWN: - if (!wdev->ssid_len) - break; - switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: cfg80211_leave_ibss(rdev, dev, true); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index c92b542..a5330c5 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -92,8 +92,12 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext) { + struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + if (!wdev->ssid_len) + return -ENOLINK; + err = rdev->ops->leave_ibss(&rdev->wiphy, dev); if (err) -- cgit v1.1 From cb0b4beb93d14429bf0c50fc1ab8e26348dca880 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:07 +0200 Subject: cfg80211: mlme API must be able to sleep After the mac80211 mlme cleanup, we can require that the MLME functions in cfg80211 can sleep. This will simplify future work in cfg80211 a lot. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 26 +++++++++----------------- net/wireless/mlme.c | 51 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 108e8c9..15dbb57 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -412,11 +412,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len, - GFP_KERNEL); + cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len); else - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len, - GFP_KERNEL); + cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len); ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1839,12 +1837,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* no action */ break; case RX_MGMT_CFG80211_DEAUTH: - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, - skb->len, GFP_KERNEL); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); break; case RX_MGMT_CFG80211_DISASSOC: - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, - skb->len, GFP_KERNEL); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); break; default: WARN(1, "unexpected: %d", rma); @@ -1893,12 +1889,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* no action */ break; case RX_MGMT_CFG80211_AUTH: - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len, - GFP_KERNEL); + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len); break; case RX_MGMT_CFG80211_ASSOC: - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len, - GFP_KERNEL); + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len); break; default: WARN(1, "unexpected: %d", rma); @@ -2026,13 +2020,11 @@ static void ieee80211_sta_work(struct work_struct *work) switch (wk->tries) { case RX_MGMT_CFG80211_AUTH_TO: cfg80211_send_auth_timeout(sdata->dev, - wk->bss->cbss.bssid, - GFP_KERNEL); + wk->bss->cbss.bssid); break; case RX_MGMT_CFG80211_ASSOC_TO: - cfg80211_send_auth_timeout(sdata->dev, - wk->bss->cbss.bssid, - GFP_KERNEL); + cfg80211_send_assoc_timeout(sdata->dev, + wk->bss->cbss.bssid); break; default: WARN(1, "unexpected: %d", wk->tries); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 087d337..f7dc752 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -12,7 +12,7 @@ #include "core.h" #include "nl80211.h" -void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) +void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -23,6 +23,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gf u16 status = le16_to_cpu(mgmt->u.auth.status_code); bool done = false; + might_sleep(); + for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, @@ -41,12 +43,12 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gf WARN_ON(!done); - nl80211_send_rx_auth(rdev, dev, buf, len, gfp); + nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); cfg80211_sme_rx_auth(dev, buf, len); } EXPORT_SYMBOL(cfg80211_send_rx_auth); -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) +void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) { u16 status_code; struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -57,12 +59,14 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); bool done; + might_sleep(); + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); - nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, gfp); + status_code, GFP_KERNEL); if (status_code == WLAN_STATUS_SUCCESS) { for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { @@ -80,7 +84,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -90,7 +94,9 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp int i; bool done = false; - nl80211_send_deauth(rdev, dev, buf, len, gfp); + might_sleep(); + + nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); if (wdev->current_bss && memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { @@ -132,16 +138,17 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, gfp, NULL, 0, + __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, reason_code, from_ap); } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); } } EXPORT_SYMBOL(cfg80211_send_deauth); -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -153,7 +160,9 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, g bool from_ap; bool done = false; - nl80211_send_disassoc(rdev, dev, buf, len, gfp); + might_sleep(); + + nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); if (!wdev->sme_state == CFG80211_SME_CONNECTED) return; @@ -177,12 +186,12 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, g reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, gfp, NULL, 0, + __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, reason_code, from_ap); } EXPORT_SYMBOL(cfg80211_send_disassoc); -void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) +void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -190,10 +199,13 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gf int i; bool done = false; - nl80211_send_auth_timeout(rdev, dev, addr, gfp); + might_sleep(); + + nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -211,7 +223,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gf } EXPORT_SYMBOL(cfg80211_send_auth_timeout); -void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) +void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -219,10 +231,13 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t g int i; bool done = false; - nl80211_send_assoc_timeout(rdev, dev, addr, gfp); + might_sleep(); + + nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i] && -- cgit v1.1 From c1e6fb1aad0d4d4f032d46b6bab093a41c6c82bf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:08 +0200 Subject: cfg80211: warn again on spurious deauth The original code in mac80211 could send a deauth frame under certain circumstances even if nothing had ever requested an authentication. This has been fixed with the rework there, so cfg80211 can now warn again about spurious events to catch possible future drivers or mac80211 regressions. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index f7dc752..960bf60 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -122,15 +122,9 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) break; } } -/* - * mac80211 currently triggers this warning, - * so disable for now (it's harmless, just - * means that we got a spurious event) WARN_ON(!done); - */ - if (wdev->sme_state == CFG80211_SME_CONNECTED) { u16 reason_code; bool from_ap; -- cgit v1.1 From 4d0c8aead32ecdbe1310ad473b3637991f560865 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:09 +0200 Subject: cfg80211: properly name driver locking Currently we call that cfg80211_put_dev(), but that is misleading. With the new convention of using 'rdev' for registered_device variables, also call that function cfg80211_unlock_rdev(). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 6 ---- net/wireless/core.h | 8 ++++-- net/wireless/nl80211.c | 74 +++++++++++++++++++++++++------------------------- net/wireless/scan.c | 6 ++-- 4 files changed, 45 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 83ec719..c6813be 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -171,12 +171,6 @@ cfg80211_get_dev_from_ifindex(int ifindex) return drv; } -void cfg80211_put_dev(struct cfg80211_registered_device *drv) -{ - BUG_ON(IS_ERR(drv)); - mutex_unlock(&drv->mtx); -} - /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) diff --git a/net/wireless/core.h b/net/wireless/core.h index 4554453..92da612 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -146,7 +146,7 @@ __cfg80211_drv_from_info(struct genl_info *info); * If successful, it returns non-NULL and also locks * the driver's mutex! * - * This means that you need to call cfg80211_put_dev() + * This means that you need to call cfg80211_unlock_rdev() * before being allowed to acquire &cfg80211_mutex! * * This is necessary because we need to lock the global @@ -170,7 +170,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); -extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) +{ + BUG_ON(IS_ERR(drv) || !drv); + mutex_unlock(&drv->mtx); +} /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44c520c..df22048 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -411,14 +411,14 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) goto out_free; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return genlmsg_unicast(msg, info->snd_pid); out_free: nlmsg_free(msg); out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -737,7 +737,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) goto out_free; dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return genlmsg_unicast(msg, info->snd_pid); @@ -745,7 +745,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) nlmsg_free(msg); out_err: dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -853,7 +853,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) unlock: dev_put(dev); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -906,7 +906,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) type, err ? NULL : &flags, ¶ms); unlock: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -934,7 +934,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); unlock_rtnl: rtnl_unlock(); return err; @@ -1037,7 +1037,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) err = -ENOBUFS; nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1097,7 +1097,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: @@ -1163,7 +1163,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1211,7 +1211,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: @@ -1306,7 +1306,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) err = call(&drv->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1338,7 +1338,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_beacon(&drv->wiphy, dev); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1572,7 +1572,7 @@ static int nl80211_dump_station(struct sk_buff *skb, cb->args[1] = sta_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -1624,7 +1624,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1760,7 +1760,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1865,7 +1865,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1904,7 +1904,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2035,7 +2035,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, cb->args[1] = path_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -2093,7 +2093,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2142,7 +2142,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2190,7 +2190,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2222,7 +2222,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) err = drv->ops->del_mpath(&drv->wiphy, dev, dst); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2278,7 +2278,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2444,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = -EMSGSIZE; out: /* Cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2550,7 +2550,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) out: /* cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2890,7 +2890,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) kfree(request); } out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -3007,7 +3007,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[1] = idx; err = skb->len; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_put_netdev: dev_put(netdev); @@ -3112,7 +3112,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) ssid, ssid_len, ie, ie_len); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3259,7 +3259,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) &crypto); out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3321,7 +3321,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3383,7 +3383,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3464,7 +3464,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) err = cfg80211_join_ibss(drv, dev, &ibss); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3501,7 +3501,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) err = cfg80211_leave_ibss(drv, dev, false); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3538,7 +3538,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) rdev->testmode_info = NULL; } - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); @@ -3707,7 +3707,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) err = cfg80211_connect(drv, dev, &connect); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3748,7 +3748,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) err = cfg80211_disconnect(drv, dev, reason, true); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(drv); dev_put(dev); unlock_rtnl: rtnl_unlock(); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9253994..1625faf 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -28,8 +28,6 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) if (!dev) goto out; - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); - /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with @@ -636,7 +634,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, } else nl80211_send_scan_start(rdev, dev); out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); @@ -945,7 +943,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, } out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return res; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); -- cgit v1.1 From 4f5dadcebb55fccef34722bbbf6401d39124c8a4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:10 +0200 Subject: cfg80211: fix MFP bug, sparse warnings sparse warns about a number of things, and one of them (use_mfp shadowed variable) actually is a bug, fix all of them. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 6 +++--- net/wireless/wext-compat.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index df22048..4976eac 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3239,11 +3239,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_USE_MFP]) { - enum nl80211_mfp use_mfp = + enum nl80211_mfp mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); - if (use_mfp == NL80211_MFP_REQUIRED) + if (mfp == NL80211_MFP_REQUIRED) use_mfp = true; - else if (use_mfp != NL80211_MFP_NO) { + else if (mfp != NL80211_MFP_NO) { err = -EINVAL; goto out; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 2c33cd8..9d101d5 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -855,7 +855,7 @@ static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) return 0; } -int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) +static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) { wdev->wext.connect.crypto.cipher_group = 0; @@ -880,7 +880,7 @@ int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) return 0; } -int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) +static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) { int nr_ciphers = 0; u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; @@ -918,7 +918,7 @@ int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) } -int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) +static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) { int nr_akm_suites = 0; -- cgit v1.1 From 667503ddcb96f3b10211f997fe55907fa7509841 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:11 +0200 Subject: cfg80211: fix locking Over time, a lot of locking issues have crept into the smarts of cfg80211, so e.g. scan completion can race against a new scan, IBSS join can race against leaving an IBSS, etc. Introduce a new per-interface lock that protects most of the per-interface data that we need to keep track of, and sprinkle assertions about that lock everywhere. Some things now need to be offloaded to work structs so that we don't require being able to sleep in functions the drivers call. The exception to that are the MLME callbacks (rx_auth etc.) that currently only mac80211 calls because it was easier to do that there instead of in cfg80211, and future drivers implementing those calls will, if they ever exist, probably need to use a similar scheme like mac80211 anyway... In order to be able to handle _deauth and _disassoc properly, introduce a cookie passed to it that will determine locking requirements. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 12 ++- net/mac80211/ieee80211_i.h | 6 +- net/mac80211/mlme.c | 25 +++-- net/wireless/core.c | 92 +++++++++++++++-- net/wireless/core.h | 100 +++++++++++++++++- net/wireless/ibss.c | 133 +++++++++++++++++++----- net/wireless/mlme.c | 214 +++++++++++++++++++++++++++++--------- net/wireless/nl80211.c | 8 +- net/wireless/nl80211.h | 2 +- net/wireless/scan.c | 30 +++++- net/wireless/sme.c | 252 +++++++++++++++++++++++++++++++++++---------- net/wireless/wext-sme.c | 125 +++++++++++++++------- 12 files changed, 804 insertions(+), 195 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7cfc14e..36f8f24 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1182,15 +1182,19 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req) + struct cfg80211_deauth_request *req, + void *cookie) { - return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req) + struct cfg80211_disassoc_request *req, + void *cookie) { - return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2e92bbd..327aabc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -918,9 +918,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req); int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req); + struct cfg80211_deauth_request *req, + void *cookie); int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req); + struct cfg80211_disassoc_request *req, + void *cookie); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_send_pspoll(struct ieee80211_local *local, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 15dbb57..c9db964 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -386,7 +386,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - const u8 *bssid, u16 stype, u16 reason) + const u8 *bssid, u16 stype, u16 reason, + void *cookie) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -412,9 +413,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie); else - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie); ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1837,10 +1838,12 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* no action */ break; case RX_MGMT_CFG80211_DEAUTH: - cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, + NULL); break; case RX_MGMT_CFG80211_DISASSOC: - cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, + NULL); break; default: WARN(1, "unexpected: %d", rma); @@ -2273,7 +2276,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req) + struct cfg80211_deauth_request *req, + void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_work *wk; @@ -2305,13 +2309,15 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, req->reason_code); + IEEE80211_STYPE_DEAUTH, req->reason_code, + cookie); return 0; } int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req) + struct cfg80211_disassoc_request *req, + void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -2331,6 +2337,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, - IEEE80211_STYPE_DISASSOC, req->reason_code); + IEEE80211_STYPE_DISASSOC, req->reason_code, + cookie); return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index c6813be..9c73769 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -257,6 +257,71 @@ static void cfg80211_rfkill_sync_work(struct work_struct *work) cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); } +static void cfg80211_process_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + __cfg80211_connect_result( + wdev->netdev, ev->cr.bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bssid, + ev->rm.req_ie, ev->rm.req_ie_len, + ev->rm.resp_ie, ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); + + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +static void cfg80211_event_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + rdev = container_of(work, struct cfg80211_registered_device, + event_work); + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) @@ -299,6 +364,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_LIST_HEAD(&drv->netdev_list); spin_lock_init(&drv->bss_lock); INIT_LIST_HEAD(&drv->bss_list); + INIT_WORK(&drv->scan_done_wk, __cfg80211_scan_done); device_initialize(&drv->wiphy.dev); drv->wiphy.dev.class = &ieee80211_class; @@ -316,6 +382,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); INIT_WORK(&drv->conn_work, cfg80211_conn_work); + INIT_WORK(&drv->event_work, cfg80211_event_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. @@ -477,6 +544,9 @@ void wiphy_unregister(struct wiphy *wiphy) mutex_unlock(&drv->mtx); cancel_work_sync(&drv->conn_work); + cancel_work_sync(&drv->scan_done_wk); + kfree(drv->scan_req); + flush_work(&drv->event_work); cfg80211_debugfs_drv_del(drv); @@ -535,6 +605,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, switch (state) { case NETDEV_REGISTER: + mutex_init(&wdev->mtx); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, @@ -566,15 +639,17 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_STATION: + wdev_lock(wdev); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.ie); wdev->wext.ie = NULL; wdev->wext.ie_len = 0; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif - cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); + __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); cfg80211_mlme_down(rdev, dev); + wdev_unlock(wdev); break; default: break; @@ -582,20 +657,24 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: if (wdev->wext.ibss.ssid_len) - cfg80211_join_ibss(rdev, dev, - &wdev->wext.ibss); + __cfg80211_join_ibss(rdev, dev, + &wdev->wext.ibss); break; case NL80211_IFTYPE_STATION: if (wdev->wext.connect.ssid_len) - cfg80211_connect(rdev, dev, - &wdev->wext.connect); + __cfg80211_connect(rdev, dev, + &wdev->wext.connect); break; default: break; } + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); #endif break; case NETDEV_UNREGISTER: @@ -605,6 +684,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, list_del_init(&wdev->list); } mutex_unlock(&rdev->devlist_mtx); + mutex_destroy(&wdev->mtx); break; case NETDEV_PRE_UP: if (rfkill_blocked(rdev->rfkill)) diff --git a/net/wireless/core.h b/net/wireless/core.h index 92da612..5ccd642 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -57,12 +57,14 @@ struct cfg80211_registered_device { u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; + struct work_struct scan_done_wk; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; #endif struct work_struct conn_work; + struct work_struct event_work; #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ @@ -170,12 +172,73 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *drv) +{ + mutex_lock(&drv->mtx); +} + static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) { BUG_ON(IS_ERR(drv) || !drv); mutex_unlock(&drv->mtx); } +static inline void wdev_lock(struct wireless_dev *wdev) + __acquires(wdev) +{ + mutex_lock(&wdev->mtx); + __acquire(wdev->mtx); +} + +static inline void wdev_unlock(struct wireless_dev *wdev) + __releases(wdev) +{ + __release(wdev->mtx); + mutex_unlock(&wdev->mtx); +} + +#define ASSERT_RDEV_LOCK(rdev) WARN_ON(!mutex_is_locked(&(rdev)->mtx)); +#define ASSERT_WDEV_LOCK(wdev) WARN_ON(!mutex_is_locked(&(wdev)->mtx)); + +enum cfg80211_event_type { + EVENT_CONNECT_RESULT, + EVENT_ROAMED, + EVENT_DISCONNECTED, + EVENT_IBSS_JOINED, +}; + +struct cfg80211_event { + struct list_head list; + enum cfg80211_event_type type; + + union { + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + u16 status; + } cr; + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + } rm; + struct { + const u8 *ie; + size_t ie_len; + u16 reason; + } dc; + struct { + u8 bssid[ETH_ALEN]; + } ij; + }; +}; + + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); @@ -191,25 +254,46 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs); /* IBSS */ +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); /* MLME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len); +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt); +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason); @@ -218,24 +302,38 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, u16 reason); void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev); +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev); /* SME */ +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect); +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len); void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); -void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); +void __cfg80211_scan_done(struct work_struct *wk); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a5330c5..99ef936 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -10,7 +10,7 @@ #include "nl80211.h" -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -39,22 +39,45 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, + GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_IBSS_JOINED; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} EXPORT_SYMBOL(cfg80211_ibss_joined); -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params) +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (wdev->ssid_len) return -EALREADY; @@ -72,10 +95,26 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return 0; } -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_ibss(rdev, dev, params); + wdev_unlock(wdev); + + return err; +} + +static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + ASSERT_WDEV_LOCK(wdev); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -89,12 +128,23 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext) #endif } -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) +void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_clear_ibss(dev, nowext); + wdev_unlock(wdev); +} + +static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->ssid_len) return -ENOLINK; @@ -103,11 +153,24 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, if (err) return err; - cfg80211_clear_ibss(dev, nowext); + __cfg80211_clear_ibss(dev, nowext); return 0; } +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ibss(rdev, dev, nowext); + wdev_unlock(wdev); + + return err; +} + #ifdef CONFIG_WIRELESS_EXT static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -184,12 +247,15 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, if (wdev->wext.ibss.channel == chan) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (chan) { wdev->wext.ibss.channel = chan; @@ -215,10 +281,12 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; + wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; + wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -247,12 +315,15 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) return -EOPNOTSUPP; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; /* iwconfig uses nul termination in SSID.. */ if (len > 0 && ssid[len - 1] == '\0') @@ -279,6 +350,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->flags = 0; + wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; @@ -288,6 +360,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->length = wdev->wext.ibss.ssid_len; memcpy(ssid, wdev->wext.ibss.ssid, data->length); } + wdev_unlock(wdev); return 0; } @@ -325,12 +398,15 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (bssid) { memcpy(wdev->wext.bssid, bssid, ETH_ALEN); @@ -355,10 +431,13 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; + wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); + wdev_unlock(wdev); + return 0; } /* temporary symbol - mark GPL - in the future the handler won't be */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 960bf60..1b2ca1f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -23,7 +23,7 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) u16 status = le16_to_cpu(mgmt->u.auth.status_code); bool done = false; - might_sleep(); + wdev_lock(wdev); for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -45,6 +45,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); cfg80211_sme_rx_auth(dev, buf, len); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); @@ -59,14 +61,15 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); bool done; - might_sleep(); + wdev_lock(wdev); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, GFP_KERNEL); + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS); if (status_code == WLAN_STATUS_SUCCESS) { for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { @@ -81,10 +84,13 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) WARN_ON(!done); } + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_deauth(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -94,7 +100,7 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) int i; bool done = false; - might_sleep(); + ASSERT_WDEV_LOCK(wdev); nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); @@ -132,17 +138,35 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, - reason_code, from_ap); + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { - cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + } +} + + +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_deauth(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_deauth(dev, buf, len); + wdev_unlock(wdev); } } EXPORT_SYMBOL(cfg80211_send_deauth); -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_disassoc(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -154,12 +178,12 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) bool from_ap; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); if (!wdev->sme_state == CFG80211_SME_CONNECTED) - return; + goto out; if (wdev->current_bss && memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { @@ -180,8 +204,26 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, - reason_code, from_ap); + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); + out: + wdev_unlock(wdev); +} + +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_disassoc(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_disassoc(dev, buf, len); + wdev_unlock(wdev); + } } EXPORT_SYMBOL(cfg80211_send_disassoc); @@ -193,13 +235,13 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) int i; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -214,6 +256,8 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) } WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -225,13 +269,13 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) int i; bool done = false; - might_sleep(); + wdev_lock(wdev); nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) - cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i] && @@ -246,6 +290,8 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) } WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); @@ -276,17 +322,21 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, EXPORT_SYMBOL(cfg80211_michael_mic_failure); /* some MLME handling for userspace SME */ -int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; struct cfg80211_internal_bss *bss; int i, err, slot = -1, nfree = 0; + ASSERT_WDEV_LOCK(wdev); + if (wdev->current_bss && memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; @@ -342,18 +392,37 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_assoc_request req; struct cfg80211_internal_bss *bss; int i, err, slot = -1; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); if (wdev->current_bss) @@ -390,14 +459,35 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, crypt); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req; int i; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); req.reason_code = reason; req.ie = ie; @@ -421,16 +511,32 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, if (!req.bss) return -ENOTCONN; - return rdev->ops->deauth(&rdev->wiphy, dev, &req); + return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); } -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason) +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; +} + +static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_disassoc_request req; + ASSERT_WDEV_LOCK(wdev); + memset(&req, 0, sizeof(req)); req.reason_code = reason; req.ie = ie; @@ -440,7 +546,21 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, else return -ENOTCONN; - return rdev->ops->disassoc(&rdev->wiphy, dev, &req); + return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; } void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, @@ -450,6 +570,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct cfg80211_deauth_request req; int i; + ASSERT_WDEV_LOCK(wdev); + if (!rdev->ops->deauth) return; @@ -460,7 +582,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, if (wdev->current_bss) { req.bss = &wdev->current_bss->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -471,7 +593,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i]) { req.bss = &wdev->auth_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->auth_bsses[i]) { cfg80211_unhold_bss(wdev->auth_bsses[i]); cfg80211_put_bss(&wdev->auth_bsses[i]->pub); @@ -480,7 +602,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, } if (wdev->authtry_bsses[i]) { req.bss = &wdev->authtry_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req); + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); if (wdev->authtry_bsses[i]) { cfg80211_unhold_bss(wdev->authtry_bsses[i]); cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4976eac..cf4ac78 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4029,6 +4029,8 @@ static int nl80211_add_scan_req(struct sk_buff *msg, struct nlattr *nest; int i; + ASSERT_RDEV_LOCK(rdev); + if (WARN_ON(!req)) return 0; @@ -4391,12 +4393,12 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, - u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp) + const u8 *ie, size_t ie_len, bool from_ap) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!msg) return; @@ -4420,7 +4422,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index cf3708b..44cc2a7 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -42,7 +42,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, - u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp); + const u8 *ie, size_t ie_len, bool from_ap); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1625faf..4f552c3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -17,13 +17,21 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +void __cfg80211_scan_done(struct work_struct *wk) { + struct cfg80211_registered_device *rdev; + struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + mutex_lock(&rdev->mtx); + request = rdev->scan_req; + dev = dev_get_by_index(&init_net, request->ifidx); if (!dev) goto out; @@ -35,7 +43,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) */ cfg80211_sme_scan_done(dev); - if (aborted) + if (request->aborted) nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); else nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); @@ -43,7 +51,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) wiphy_to_dev(request->wiphy)->scan_req = NULL; #ifdef CONFIG_WIRELESS_EXT - if (!aborted) { + if (!request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); @@ -53,8 +61,24 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) dev_put(dev); out: + cfg80211_unlock_rdev(rdev); kfree(request); } + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); + if (WARN_ON(!dev)) { + kfree(request); + return; + } + + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + + request->aborted = aborted; + schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); + dev_put(dev); +} EXPORT_SYMBOL(cfg80211_scan_done); static void bss_release(struct kref *ref) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 066a19e..472e241 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -38,6 +38,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) int n_channels, err; ASSERT_RTNL(); + ASSERT_RDEV_LOCK(drv); + ASSERT_WDEV_LOCK(wdev); if (drv->scan_req) return -EBUSY; @@ -106,6 +108,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) struct cfg80211_connect_params *params; int err; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->conn) return 0; @@ -117,11 +121,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_AUTHENTICATE_NEXT: BUG_ON(!drv->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return cfg80211_mlme_auth(drv, wdev->netdev, - params->channel, params->auth_type, - params->bssid, - params->ssid, params->ssid_len, - NULL, 0); + return __cfg80211_mlme_auth(drv, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!drv->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -131,14 +135,16 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) * that some APs don't like that -- so we'd need to retry * the association. */ - err = cfg80211_mlme_assoc(drv, wdev->netdev, - params->channel, params->bssid, NULL, - params->ssid, params->ssid_len, - params->ie, params->ie_len, - false, ¶ms->crypto); + err = __cfg80211_mlme_assoc(drv, wdev->netdev, + params->channel, params->bssid, + NULL, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); if (err) - cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING); + __cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING); return err; default: return 0; @@ -152,22 +158,31 @@ void cfg80211_conn_work(struct work_struct *work) struct wireless_dev *wdev; rtnl_lock(); + cfg80211_lock_rdev(drv); mutex_lock(&drv->devlist_mtx); list_for_each_entry(wdev, &drv->netdev_list, list) { - if (!netif_running(wdev->netdev)) + wdev_lock(wdev); + if (!netif_running(wdev->netdev)) { + wdev_unlock(wdev); continue; - if (wdev->sme_state != CFG80211_SME_CONNECTING) + } + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wdev_unlock(wdev); continue; + } if (cfg80211_conn_do_work(wdev)) - cfg80211_connect_result(wdev->netdev, - wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + wdev_unlock(wdev); } mutex_unlock(&drv->devlist_mtx); + cfg80211_unlock_rdev(drv); rtnl_unlock(); } @@ -177,6 +192,8 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) struct cfg80211_bss *bss; u16 capa = WLAN_CAPABILITY_ESS; + ASSERT_WDEV_LOCK(wdev); + if (wdev->conn->params.privacy) capa |= WLAN_CAPABILITY_PRIVACY; @@ -198,11 +215,13 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) return true; } -void cfg80211_sme_scan_done(struct net_device *dev) +static void __cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + ASSERT_WDEV_LOCK(wdev); + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -218,15 +237,26 @@ void cfg80211_sme_scan_done(struct net_device *dev) if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) schedule_work(&drv->conn_work); else - cfg80211_connect_result(dev, wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); - return; + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); } } -void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_sme_scan_done(dev); + wdev_unlock(wdev); +} + +void cfg80211_sme_rx_auth(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -234,6 +264,8 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + ASSERT_WDEV_LOCK(wdev); + /* should only RX auth frames when connecting */ if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -273,10 +305,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) } } -static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, gfp_t gfp) +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -284,18 +316,20 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; if (wdev->sme_state == CFG80211_SME_CONNECTED) nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, gfp); + resp_ie, resp_ie_len, GFP_KERNEL); else nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, - status, gfp); + status, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (wextev) { @@ -362,21 +396,43 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp) { - bool wextev = status == WLAN_STATUS_SUCCESS; - __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp); + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_CONNECT_RESULT; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->cr.req_ie_len = req_ie_len; + memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->cr.resp_ie_len = resp_ie_len; + memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + ev->cr.status = status; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); } EXPORT_SYMBOL(cfg80211_connect_result); -void cfg80211_roamed(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len) { - struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; @@ -402,31 +458,62 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (req_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, req_ie); } if (resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_ROAMED; + memcpy(ev->rm.bssid, bssid, ETH_ALEN); + ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->rm.req_ie_len = req_ie_len; + memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); + ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->rm.resp_ie_len = resp_ie_len; + memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} EXPORT_SYMBOL(cfg80211_roamed); -void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -434,6 +521,8 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, union iwreq_data wrqu; #endif + ASSERT_WDEV_LOCK(wdev); + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; @@ -456,7 +545,7 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, } nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, - reason, ie, ie_len, from_ap, gfp); + reason, ie, ie_len, from_ap); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); @@ -468,16 +557,36 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { - __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_DISCONNECTED; + ev->dc.ie = ((u8 *)ev) + sizeof(*ev); + ev->dc.ie_len = ie_len; + memcpy((void *)ev->dc.ie, ie, ie_len); + ev->dc.reason = reason; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); } EXPORT_SYMBOL(cfg80211_disconnected); -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect) +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) { - int err; struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; @@ -572,12 +681,27 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, } } -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, bool wextev) +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_connect(rdev, dev, connect); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; @@ -601,8 +725,9 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, } /* wdev->conn->params.bssid must be set if > SCANNING */ - err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, - NULL, 0, reason); + err = __cfg80211_mlme_deauth(rdev, dev, + wdev->conn->params.bssid, + NULL, 0, reason); if (err) return err; } else { @@ -612,21 +737,36 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, } if (wdev->sme_state == CFG80211_SME_CONNECTED) - __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); + __cfg80211_disconnected(dev, NULL, 0, 0, false); else if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev, GFP_KERNEL); + wextev); return 0; } +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + u16 reason, bool wextev) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_disconnect(rdev, dev, reason, wextev); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + void cfg80211_sme_disassoc(struct net_device *dev, int idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 bssid[ETH_ALEN]; + ASSERT_WDEV_LOCK(wdev); + if (!wdev->conn) return; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1987a..6f75aaa7f 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -15,6 +15,9 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, { int err; + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + if (!netif_running(wdev->netdev)) return 0; @@ -24,8 +27,8 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, err = 0; if (wdev->wext.connect.ssid_len != 0) - err = cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect); + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect); return err; } @@ -50,33 +53,43 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) return -EINVAL; - if (wdev->wext.connect.channel == chan) - return 0; + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); + + if (wdev->wext.connect.channel == chan) { + err = 0; + goto out; + } if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); if (err) - return err; + goto out; } + wdev->wext.connect.channel = chan; /* SSID is not set, we just want to switch channel */ if (wdev->wext.connect.ssid_len && chan) { - if (!rdev->ops->set_channel) - return -EOPNOTSUPP; - - return rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); + err = -EOPNOTSUPP; + if (rdev->ops->set_channel) + err = rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + goto out; } - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); @@ -92,10 +105,12 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; + wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; + wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -128,21 +143,26 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; + if (wdev->wext.connect.ssid && len && len == wdev->wext.connect.ssid_len && memcmp(wdev->wext.connect.ssid, ssid, len)) - return 0; + goto out; if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); if (err) - return err; + goto out; } wdev->wext.connect.ssid = wdev->wext.ssid; @@ -151,7 +171,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, wdev->wext.connect.crypto.control_port = false; - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); @@ -168,6 +192,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->flags = 0; + wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; @@ -178,6 +203,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, memcpy(ssid, wdev->wext.connect.ssid, data->length); } else data->flags = 0; + wdev_unlock(wdev); return 0; } @@ -203,21 +229,25 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; /* both automatic */ if (!bssid && !wdev->wext.connect.bssid) - return 0; + goto out; /* fixed already - and no change */ if (wdev->wext.connect.bssid && bssid && compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) - return 0; + goto out; if (wdev->sme_state != CFG80211_SME_IDLE) { - err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - false); + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + false); if (err) - return err; + goto out; } if (bssid) { @@ -226,7 +256,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, } else wdev->wext.connect.bssid = NULL; - return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); @@ -243,12 +277,14 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; + wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); else if (wdev->wext.connect.bssid) memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); else memset(ap_addr->sa_data, 0, ETH_ALEN); + wdev_unlock(wdev); return 0; } @@ -270,15 +306,20 @@ int cfg80211_wext_siwgenie(struct net_device *dev, if (!ie_len) ie = NULL; + wdev_lock(wdev); + /* no change */ + err = 0; if (wdev->wext.ie_len == ie_len && memcmp(wdev->wext.ie, ie, ie_len) == 0) - return 0; + goto out; if (ie_len) { ie = kmemdup(extra, ie_len, GFP_KERNEL); - if (!ie) - return -ENOMEM; + if (!ie) { + err = -ENOMEM; + goto out; + } } else ie = NULL; @@ -287,14 +328,17 @@ int cfg80211_wext_siwgenie(struct net_device *dev, wdev->wext.ie_len = ie_len; if (wdev->sme_state != CFG80211_SME_IDLE) { - err = cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) - return err; + goto out; } /* userspace better not think we'll reconnect */ - return 0; + err = 0; + out: + wdev_unlock(wdev); + return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); @@ -305,6 +349,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_mlme *mlme = (struct iw_mlme *)extra; struct cfg80211_registered_device *rdev; + int err; if (!wdev) return -EOPNOTSUPP; @@ -317,13 +362,19 @@ int cfg80211_wext_siwmlme(struct net_device *dev, if (mlme->addr.sa_family != ARPHRD_ETHER) return -EINVAL; + wdev_lock(wdev); switch (mlme->cmd) { case IW_MLME_DEAUTH: case IW_MLME_DISASSOC: - return cfg80211_disconnect(rdev, dev, mlme->reason_code, - true); + err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + break; default: - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + break; } + wdev_unlock(wdev); + + return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); -- cgit v1.1 From 79c97e97aed7f760d2826c7daf2d42d8eefe9838 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:12 +0200 Subject: cfg80211: clean up naming once and for all We've named the registered devices 'drv' sometimes, thinking of "driver", which is not what it is, it's the internal representation of a wiphy, i.e. a device. Let's clean up the naming once and and use 'rdev' aka 'registered device' everywhere. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 228 ++++++++++++++++----------------- net/wireless/core.h | 26 ++-- net/wireless/debugfs.c | 14 +- net/wireless/debugfs.h | 8 +- net/wireless/nl80211.c | 340 ++++++++++++++++++++++++------------------------- net/wireless/reg.c | 48 +++---- net/wireless/sme.c | 48 +++---- 7 files changed, 356 insertions(+), 356 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 9c73769..b5e2f6d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -30,10 +30,10 @@ MODULE_DESCRIPTION("wireless configuration support"); /* RCU might be appropriate here since we usually * only read the list, and that can happen quite * often because we need to do it for each command */ -LIST_HEAD(cfg80211_drv_list); +LIST_HEAD(cfg80211_rdev_list); /* - * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, + * This is used to protect the cfg80211_rdev_list, cfg80211_regdomain, * country_ie_regdomain, the reg_beacon_list and the the last regulatory * request receipt (last_request). */ @@ -43,18 +43,18 @@ DEFINE_MUTEX(cfg80211_mutex); static struct dentry *ieee80211_debugfs_dir; /* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { - struct cfg80211_registered_device *result = NULL, *drv; + struct cfg80211_registered_device *result = NULL, *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - list_for_each_entry(drv, &cfg80211_drv_list, list) { - if (drv->wiphy_idx == wiphy_idx) { - result = drv; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy_idx == wiphy_idx) { + result = rdev; break; } } @@ -64,32 +64,32 @@ struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy) return WIPHY_IDX_STALE; - drv = wiphy_to_dev(wiphy); - return drv->wiphy_idx; + rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; } -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - drv = cfg80211_drv_by_wiphy_idx(wiphy_idx); - if (!drv) + rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); + if (!rdev) return NULL; - return &drv->wiphy; + return &rdev->wiphy; } /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info) +__cfg80211_rdev_from_info(struct genl_info *info) { int ifindex; struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; @@ -99,7 +99,7 @@ __cfg80211_drv_from_info(struct genl_info *info) assert_cfg80211_lock(); if (info->attrs[NL80211_ATTR_WIPHY]) { - bywiphyidx = cfg80211_drv_by_wiphy_idx( + bywiphyidx = cfg80211_rdev_by_wiphy_idx( nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); err = -ENODEV; } @@ -134,26 +134,26 @@ __cfg80211_drv_from_info(struct genl_info *info) struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; mutex_lock(&cfg80211_mutex); - drv = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); /* if it is not an error we grab the lock on * it to assure it won't be going away while * we operate on it */ - if (!IS_ERR(drv)) - mutex_lock(&drv->mtx); + if (!IS_ERR(rdev)) + mutex_lock(&rdev->mtx); mutex_unlock(&cfg80211_mutex); - return drv; + return rdev; } struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex) { - struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV); + struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); @@ -161,21 +161,21 @@ cfg80211_get_dev_from_ifindex(int ifindex) if (!dev) goto out; if (dev->ieee80211_ptr) { - drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&drv->mtx); + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); } else - drv = ERR_PTR(-ENODEV); + rdev = ERR_PTR(-ENODEV); dev_put(dev); out: mutex_unlock(&cfg80211_mutex); - return drv; + return rdev; } /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, result, digits; assert_cfg80211_lock(); @@ -201,8 +201,8 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; /* Ensure another device does not already have this name. */ - list_for_each_entry(drv, &cfg80211_drv_list, list) - if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) return -EINVAL; result = device_rename(&rdev->wiphy.dev, newname); @@ -224,26 +224,26 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; - drv->ops->rfkill_poll(&drv->wiphy); + rdev->ops->rfkill_poll(&rdev->wiphy); } static int cfg80211_rfkill_set_block(void *data, bool blocked) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; struct wireless_dev *wdev; if (!blocked) return 0; rtnl_lock(); - mutex_lock(&drv->devlist_mtx); + mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &drv->netdev_list, list) + list_for_each_entry(wdev, &rdev->netdev_list, list) dev_close(wdev->netdev); - mutex_unlock(&drv->devlist_mtx); + mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); return 0; @@ -251,10 +251,10 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) static void cfg80211_rfkill_sync_work(struct work_struct *work) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; - drv = container_of(work, struct cfg80211_registered_device, rfkill_sync); - cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); + rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); + cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); } static void cfg80211_process_events(struct wireless_dev *wdev) @@ -328,79 +328,79 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int alloc_size; WARN_ON(!ops->add_key && ops->del_key); WARN_ON(ops->add_key && !ops->del_key); - alloc_size = sizeof(*drv) + sizeof_priv; + alloc_size = sizeof(*rdev) + sizeof_priv; - drv = kzalloc(alloc_size, GFP_KERNEL); - if (!drv) + rdev = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev) return NULL; - drv->ops = ops; + rdev->ops = ops; mutex_lock(&cfg80211_mutex); - drv->wiphy_idx = wiphy_counter++; + rdev->wiphy_idx = wiphy_counter++; - if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) { + if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ - kfree(drv); + kfree(rdev); return NULL; } mutex_unlock(&cfg80211_mutex); /* give it a proper name */ - dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx); - - 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); - INIT_WORK(&drv->scan_done_wk, __cfg80211_scan_done); - - device_initialize(&drv->wiphy.dev); - drv->wiphy.dev.class = &ieee80211_class; - drv->wiphy.dev.platform_data = drv; - - drv->rfkill_ops.set_block = cfg80211_rfkill_set_block; - drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev), - &drv->wiphy.dev, RFKILL_TYPE_WLAN, - &drv->rfkill_ops, drv); - - if (!drv->rfkill) { - kfree(drv); + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + + mutex_init(&rdev->mtx); + mutex_init(&rdev->devlist_mtx); + INIT_LIST_HEAD(&rdev->netdev_list); + spin_lock_init(&rdev->bss_lock); + INIT_LIST_HEAD(&rdev->bss_list); + INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); + + device_initialize(&rdev->wiphy.dev); + rdev->wiphy.dev.class = &ieee80211_class; + rdev->wiphy.dev.platform_data = rdev; + + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; + rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), + &rdev->wiphy.dev, RFKILL_TYPE_WLAN, + &rdev->rfkill_ops, rdev); + + if (!rdev->rfkill) { + kfree(rdev); return NULL; } - INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); - INIT_WORK(&drv->conn_work, cfg80211_conn_work); - INIT_WORK(&drv->event_work, cfg80211_event_work); + INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); + INIT_WORK(&rdev->event_work, cfg80211_event_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the * special -1 value. */ - drv->wiphy.retry_short = 7; - drv->wiphy.retry_long = 4; - drv->wiphy.frag_threshold = (u32) -1; - drv->wiphy.rts_threshold = (u32) -1; + rdev->wiphy.retry_short = 7; + rdev->wiphy.retry_long = 4; + rdev->wiphy.frag_threshold = (u32) -1; + rdev->wiphy.rts_threshold = (u32) -1; - return &drv->wiphy; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); int wiphy_register(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); int res; enum ieee80211_band band; struct ieee80211_supported_band *sband; @@ -454,11 +454,11 @@ int wiphy_register(struct wiphy *wiphy) /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); - res = device_add(&drv->wiphy.dev); + res = device_add(&rdev->wiphy.dev); if (res) return res; - res = rfkill_register(drv->rfkill); + res = rfkill_register(rdev->rfkill); if (res) goto out_rm_dev; @@ -467,16 +467,16 @@ int wiphy_register(struct wiphy *wiphy) /* set up regulatory info */ wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); - list_add(&drv->list, &cfg80211_drv_list); + list_add(&rdev->list, &cfg80211_rdev_list); mutex_unlock(&cfg80211_mutex); /* add to debugfs */ - drv->wiphy.debugfsdir = - debugfs_create_dir(wiphy_name(&drv->wiphy), + rdev->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&rdev->wiphy), ieee80211_debugfs_dir); - if (IS_ERR(drv->wiphy.debugfsdir)) - drv->wiphy.debugfsdir = NULL; + if (IS_ERR(rdev->wiphy.debugfsdir)) + rdev->wiphy.debugfsdir = NULL; if (wiphy->custom_regulatory) { struct regulatory_request request; @@ -489,48 +489,48 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } - cfg80211_debugfs_drv_add(drv); + cfg80211_debugfs_rdev_add(rdev); return 0; out_rm_dev: - device_del(&drv->wiphy.dev); + device_del(&rdev->wiphy.dev); return res; } EXPORT_SYMBOL(wiphy_register); void wiphy_rfkill_start_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (!drv->ops->rfkill_poll) + if (!rdev->ops->rfkill_poll) return; - drv->rfkill_ops.poll = cfg80211_rfkill_poll; - rfkill_resume_polling(drv->rfkill); + rdev->rfkill_ops.poll = cfg80211_rfkill_poll; + rfkill_resume_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_start_polling); void wiphy_rfkill_stop_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_pause_polling(drv->rfkill); + rfkill_pause_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_stop_polling); void wiphy_unregister(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_unregister(drv->rfkill); + rfkill_unregister(rdev->rfkill); /* protect the device list */ mutex_lock(&cfg80211_mutex); - BUG_ON(!list_empty(&drv->netdev_list)); + BUG_ON(!list_empty(&rdev->netdev_list)); /* - * Try to grab drv->mtx. If a command is still in progress, + * Try to grab rdev->mtx. If a command is still in progress, * hopefully the driver will refuse it since it's tearing * down the device already. We wait for this command to complete * before unlinking the item from the list. @@ -539,38 +539,38 @@ void wiphy_unregister(struct wiphy *wiphy) * get to lock contention here if userspace issues a command * that identified the hardware by wiphy index. */ - mutex_lock(&drv->mtx); + mutex_lock(&rdev->mtx); /* unlock again before freeing */ - mutex_unlock(&drv->mtx); + mutex_unlock(&rdev->mtx); - cancel_work_sync(&drv->conn_work); - cancel_work_sync(&drv->scan_done_wk); - kfree(drv->scan_req); - flush_work(&drv->event_work); + cancel_work_sync(&rdev->conn_work); + cancel_work_sync(&rdev->scan_done_wk); + kfree(rdev->scan_req); + flush_work(&rdev->event_work); - cfg80211_debugfs_drv_del(drv); + cfg80211_debugfs_rdev_del(rdev); /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&drv->list); - device_del(&drv->wiphy.dev); - debugfs_remove(drv->wiphy.debugfsdir); + list_del(&rdev->list); + device_del(&rdev->wiphy.dev); + debugfs_remove(rdev->wiphy.debugfsdir); mutex_unlock(&cfg80211_mutex); } EXPORT_SYMBOL(wiphy_unregister); -void cfg80211_dev_free(struct cfg80211_registered_device *drv) +void cfg80211_dev_free(struct cfg80211_registered_device *rdev) { struct cfg80211_internal_bss *scan, *tmp; - rfkill_destroy(drv->rfkill); - mutex_destroy(&drv->mtx); - mutex_destroy(&drv->devlist_mtx); - list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) + rfkill_destroy(rdev->rfkill); + mutex_destroy(&rdev->mtx); + mutex_destroy(&rdev->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); - kfree(drv); + kfree(rdev); } void wiphy_free(struct wiphy *wiphy) @@ -581,10 +581,10 @@ EXPORT_SYMBOL(wiphy_free); void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (rfkill_set_hw_state(drv->rfkill, blocked)) - schedule_work(&drv->rfkill_sync); + if (rfkill_set_hw_state(rdev->rfkill, blocked)) + schedule_work(&rdev->rfkill_sync); } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); diff --git a/net/wireless/core.h b/net/wireless/core.h index 5ccd642..e46cd6e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -97,13 +97,13 @@ bool wiphy_idx_valid(int wiphy_idx) } extern struct mutex cfg80211_mutex; -extern struct list_head cfg80211_drv_list; +extern struct list_head cfg80211_rdev_list; #define assert_cfg80211_lock() WARN_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 + * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL */ #define WIPHY_IDX_STALE -1 @@ -136,11 +136,11 @@ static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) } -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info); +__cfg80211_rdev_from_info(struct genl_info *info); /* * This function returns a pointer to the driver @@ -153,7 +153,7 @@ __cfg80211_drv_from_info(struct genl_info *info); * * This is necessary because we need to lock the global * mutex to get an item off the list safely, and then - * we lock the drv mutex so it doesn't go away under us. + * we lock the rdev mutex so it doesn't go away under us. * * We don't want to keep cfg80211_mutex locked * for all the time in order to allow requests on @@ -165,22 +165,22 @@ __cfg80211_drv_from_info(struct genl_info *info); extern struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info); -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_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); -static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *drv) +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { - mutex_lock(&drv->mtx); + mutex_lock(&rdev->mtx); } -static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *drv) +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) { - BUG_ON(IS_ERR(drv) || !drv); - mutex_unlock(&drv->mtx); + BUG_ON(IS_ERR(rdev) || !rdev); + mutex_unlock(&rdev->mtx); } static inline void wdev_lock(struct wireless_dev *wdev) @@ -240,9 +240,9 @@ struct cfg80211_event { /* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); +extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); -extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, +extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 679ddfc..13d93d8 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -104,15 +104,15 @@ static const struct file_operations ht40allow_map_ops = { }; #define DEBUGFS_ADD(name) \ - drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ - &drv->wiphy, &name## _ops); + rdev->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ + &rdev->wiphy, &name## _ops); #define DEBUGFS_DEL(name) \ - debugfs_remove(drv->debugfs.name); \ - drv->debugfs.name = NULL; + debugfs_remove(rdev->debugfs.name); \ + rdev->debugfs.name = NULL; -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) { - struct dentry *phyd = drv->wiphy.debugfsdir; + struct dentry *phyd = rdev->wiphy.debugfsdir; DEBUGFS_ADD(rts_threshold); DEBUGFS_ADD(fragmentation_threshold); @@ -121,7 +121,7 @@ void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) DEBUGFS_ADD(ht40allow_map); } -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) { DEBUGFS_DEL(rts_threshold); DEBUGFS_DEL(fragmentation_threshold); diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h index c226983..6419b6d 100644 --- a/net/wireless/debugfs.h +++ b/net/wireless/debugfs.h @@ -2,13 +2,13 @@ #define __CFG80211_DEBUGFS_H #ifdef CONFIG_CFG80211_DEBUGFS -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv); -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv); +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev); #else static inline -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} static inline -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) {} #endif #endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cf4ac78..9deb12f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -29,9 +29,9 @@ static struct genl_family nl80211_fam = { .maxattr = NL80211_ATTR_MAX, }; -/* internal helper: get drv and dev */ -static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, - struct cfg80211_registered_device **drv, +/* internal helper: get rdev and dev */ +static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, + struct cfg80211_registered_device **rdev, struct net_device **dev) { int ifindex; @@ -44,10 +44,10 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, if (!*dev) return -ENODEV; - *drv = cfg80211_get_dev_from_ifindex(ifindex); - if (IS_ERR(*drv)) { + *rdev = cfg80211_get_dev_from_ifindex(ifindex); + if (IS_ERR(*rdev)) { dev_put(*dev); - return PTR_ERR(*drv); + return PTR_ERR(*rdev); } return 0; @@ -378,7 +378,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) struct cfg80211_registered_device *dev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -460,7 +460,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); if (IS_ERR(rdev)) { mutex_unlock(&cfg80211_mutex); result = PTR_ERR(rdev); @@ -683,7 +683,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (wp_idx < wp_start) { wp_idx++; continue; @@ -724,7 +724,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); if (err) return err; @@ -780,7 +780,7 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; int err; enum nl80211_iftype otype, ntype; @@ -792,7 +792,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -808,8 +808,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } } - if (!drv->ops->change_virtual_intf || - !(drv->wiphy.interface_modes & (1 << ntype))) { + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) { err = -EOPNOTSUPP; goto unlock; } @@ -839,7 +839,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = drv->ops->change_virtual_intf(&drv->wiphy, dev, + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, ntype, flags, ¶ms); else err = 0; @@ -853,7 +853,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) unlock: dev_put(dev); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -861,7 +861,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; @@ -880,14 +880,14 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - drv = cfg80211_get_dev_from_info(info); - if (IS_ERR(drv)) { - err = PTR_ERR(drv); + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); goto unlock_rtnl; } - if (!drv->ops->add_virtual_intf || - !(drv->wiphy.interface_modes & (1 << type))) { + if (!rdev->ops->add_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << type))) { err = -EOPNOTSUPP; goto unlock; } @@ -901,12 +901,12 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - err = drv->ops->add_virtual_intf(&drv->wiphy, + err = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); unlock: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -914,27 +914,27 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int ifindex, err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; ifindex = dev->ifindex; dev_put(dev); - if (!drv->ops->del_virtual_intf) { + if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -968,7 +968,7 @@ static void get_key_callback(void *c, struct key_params *params) static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -990,11 +990,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->get_key) { + if (!rdev->ops->get_key) { err = -EOPNOTSUPP; goto out; } @@ -1020,7 +1020,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (mac_addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, + err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr, &cookie, get_key_callback); if (err) @@ -1037,7 +1037,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) err = -ENOBUFS; nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1047,7 +1047,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx; @@ -1072,24 +1072,24 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) - func = drv->ops->set_default_key; + func = rdev->ops->set_default_key; else - func = drv->ops->set_default_mgmt_key; + func = rdev->ops->set_default_mgmt_key; if (!func) { err = -EOPNOTSUPP; goto out; } - err = func(&drv->wiphy, dev, key_idx); + err = func(&rdev->wiphy, dev, key_idx); #ifdef CONFIG_WIRELESS_EXT if (!err) { - if (func == drv->ops->set_default_key) + if (func == rdev->ops->set_default_key) dev->ieee80211_ptr->wext.default_key = key_idx; else dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; @@ -1097,7 +1097,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1108,7 +1108,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err, i; struct net_device *dev; struct key_params params; @@ -1143,27 +1143,27 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < drv->wiphy.n_cipher_suites; i++) - if (params.cipher == drv->wiphy.cipher_suites[i]) + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params.cipher == rdev->wiphy.cipher_suites[i]) break; - if (i == drv->wiphy.n_cipher_suites) { + if (i == rdev->wiphy.n_cipher_suites) { err = -EINVAL; goto out; } - if (!drv->ops->add_key) { + if (!rdev->ops->add_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); + err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1173,7 +1173,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -1190,16 +1190,16 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_key) { + if (!rdev->ops->del_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); + err = rdev->ops->del_key(&rdev->wiphy, dev, key_idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1211,7 +1211,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1224,7 +1224,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) { int (*call)(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info); - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct beacon_parameters params; @@ -1235,7 +1235,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1254,10 +1254,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - call = drv->ops->add_beacon; + call = rdev->ops->add_beacon; break; case NL80211_CMD_SET_BEACON: - call = drv->ops->set_beacon; + call = rdev->ops->set_beacon; break; default: WARN_ON(1); @@ -1303,10 +1303,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - err = call(&drv->wiphy, dev, ¶ms); + err = call(&rdev->wiphy, dev, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1316,17 +1316,17 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_beacon) { + if (!rdev->ops->del_beacon) { err = -EOPNOTSUPP; goto out; } @@ -1335,10 +1335,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_beacon(&drv->wiphy, dev); + err = rdev->ops->del_beacon(&rdev->wiphy, dev); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1581,7 +1581,7 @@ static int nl80211_dump_station(struct sk_buff *skb, static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_info sinfo; @@ -1597,16 +1597,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_station) { + if (!rdev->ops->get_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); + err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); if (err) goto out; @@ -1624,7 +1624,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1655,7 +1655,7 @@ static int get_vlan(struct nlattr *vlanattr, static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1697,11 +1697,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1750,17 +1750,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->change_station) { + if (!rdev->ops->change_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1770,7 +1770,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1810,11 +1810,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1850,7 +1850,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->add_station) { + if (!rdev->ops->add_station) { err = -EOPNOTSUPP; goto out; } @@ -1860,12 +1860,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1875,7 +1875,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *mac_addr = NULL; @@ -1885,7 +1885,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; @@ -1896,15 +1896,15 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) goto out; } - if (!drv->ops->del_station) { + if (!rdev->ops->del_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); + err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2044,7 +2044,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct mpath_info pinfo; @@ -2061,11 +2061,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mpath) { + if (!rdev->ops->get_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2075,7 +2075,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); + err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); if (err) goto out; @@ -2093,7 +2093,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) out_free: nlmsg_free(msg); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2103,7 +2103,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2120,11 +2120,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_mpath) { + if (!rdev->ops->change_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2139,10 +2139,10 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2151,7 +2151,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2168,11 +2168,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->add_mpath) { + if (!rdev->ops->add_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2187,10 +2187,10 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2200,7 +2200,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2210,19 +2210,19 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->del_mpath) { + if (!rdev->ops->del_mpath) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_mpath(&drv->wiphy, dev, dst); + err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2232,7 +2232,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct bss_parameters params; @@ -2261,11 +2261,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_bss) { + if (!rdev->ops->change_bss) { err = -EOPNOTSUPP; goto out; } @@ -2275,10 +2275,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); + err = rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2369,7 +2369,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) static int nl80211_get_mesh_params(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct mesh_config cur_params; int err; struct net_device *dev; @@ -2380,17 +2380,17 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mesh_params) { + if (!rdev->ops->get_mesh_params) { err = -EOPNOTSUPP; goto out; } /* Get the mesh params */ - err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); + err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params); if (err) goto out; @@ -2444,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = -EMSGSIZE; out: /* Cleanup */ - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2482,7 +2482,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) { int err; u32 mask; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct mesh_config cfg; struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; @@ -2497,11 +2497,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->set_mesh_params) { + if (!rdev->ops->set_mesh_params) { err = -EOPNOTSUPP; goto out; } @@ -2546,11 +2546,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) nla_get_u16); /* Apply changes */ - err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); + err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); out: /* cleanup */ - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2737,7 +2737,7 @@ static int validate_scan_freqs(struct nlattr *freqs) static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_scan_request *request; struct cfg80211_ssid *ssid; @@ -2753,13 +2753,13 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; - if (!drv->ops->scan) { + if (!rdev->ops->scan) { err = -EOPNOTSUPP; goto out; } @@ -2769,7 +2769,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - if (drv->scan_req) { + if (rdev->scan_req) { err = -EBUSY; goto out; } @@ -2876,21 +2876,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } request->ifidx = dev->ifindex; - request->wiphy = &drv->wiphy; + request->wiphy = &rdev->wiphy; - drv->scan_req = request; - err = drv->ops->scan(&drv->wiphy, dev, request); + rdev->scan_req = request; + err = rdev->ops->scan(&rdev->wiphy, dev, request); if (!err) - nl80211_send_scan_start(drv, dev); + nl80211_send_scan_start(rdev, dev); out_free: if (err) { - drv->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -3043,7 +3043,7 @@ static bool nl80211_valid_cipher_suite(u32 cipher) static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct ieee80211_channel *chan; const u8 *bssid, *ssid, *ie = NULL; @@ -3067,11 +3067,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->auth) { + if (!rdev->ops->auth) { err = -EOPNOTSUPP; goto out; } @@ -3087,7 +3087,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) } bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&drv->wiphy, + chan = ieee80211_get_channel(&rdev->wiphy, nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { err = -EINVAL; @@ -3108,11 +3108,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid, + err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3202,7 +3202,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3268,7 +3268,7 @@ unlock_rtnl: static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; const u8 *ie = NULL, *bssid; int err, ie_len = 0; @@ -3285,11 +3285,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->deauth) { + if (!rdev->ops->deauth) { err = -EOPNOTSUPP; goto out; } @@ -3318,10 +3318,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code); + err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3330,7 +3330,7 @@ unlock_rtnl: static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; const u8 *ie = NULL, *bssid; int err, ie_len = 0; @@ -3347,11 +3347,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->disassoc) { + if (!rdev->ops->disassoc) { err = -EOPNOTSUPP; goto out; } @@ -3380,10 +3380,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code); + err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3392,7 +3392,7 @@ unlock_rtnl: static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; @@ -3419,11 +3419,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->join_ibss) { + if (!rdev->ops->join_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3438,7 +3438,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_MAC]) ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -3461,10 +3461,10 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; - err = cfg80211_join_ibss(drv, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3473,17 +3473,17 @@ unlock_rtnl: static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; int err; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->leave_ibss) { + if (!rdev->ops->leave_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3498,10 +3498,10 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_leave_ibss(drv, dev, false); + err = cfg80211_leave_ibss(rdev, dev, false); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3632,7 +3632,7 @@ EXPORT_SYMBOL(cfg80211_testmode_event); static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_connect_params connect; struct wiphy *wiphy; @@ -3663,7 +3663,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3677,7 +3677,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; connect.bssid = NULL; connect.channel = NULL; @@ -3704,10 +3704,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_connect(drv, dev, &connect); + err = cfg80211_connect(rdev, dev, &connect); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3716,7 +3716,7 @@ unlock_rtnl: static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; int err; u16 reason; @@ -3731,7 +3731,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3745,10 +3745,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_disconnect(drv, dev, reason, true); + err = cfg80211_disconnect(rdev, dev, reason, true); out: - cfg80211_unlock_rdev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 5e14371..2b4a6c6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1061,10 +1061,10 @@ static bool ignore_reg_update(struct wiphy *wiphy, static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; - list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_regulatory(&drv->wiphy, initiator); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_regulatory(&rdev->wiphy, initiator); } static void handle_reg_beacon(struct wiphy *wiphy, @@ -1614,7 +1614,7 @@ static void reg_process_pending_hints(void) /* 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 cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; mutex_lock(&cfg80211_mutex); @@ -1633,8 +1633,8 @@ static void reg_process_pending_beacon_hints(void) 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); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); /* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); @@ -1814,23 +1814,23 @@ void regulatory_hint_11d(struct wiphy *wiphy, if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy_idx_valid(last_request->wiphy_idx))) { - struct cfg80211_registered_device *drv_last_ie; + struct cfg80211_registered_device *rdev_last_ie; - drv_last_ie = - cfg80211_drv_by_wiphy_idx(last_request->wiphy_idx); + rdev_last_ie = + cfg80211_rdev_by_wiphy_idx(last_request->wiphy_idx); /* * Lets keep this simple -- we trust the first AP * after we intersect with CRDA */ - if (likely(&drv_last_ie->wiphy == wiphy)) { + if (likely(&rdev_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, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* @@ -1846,9 +1846,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, * Ignore IEs coming in on two separate wiphys with * the same alpha2 and environment cap */ - if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* We could potentially intersect though */ @@ -1995,14 +1995,14 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - struct cfg80211_registered_device *drv; - drv = cfg80211_drv_by_wiphy_idx( + struct cfg80211_registered_device *rdev; + rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); - if (drv) { + if (rdev) { printk(KERN_INFO "cfg80211: Current regulatory " "domain updated by AP to: %c%c\n", - drv->country_ie_alpha2[0], - drv->country_ie_alpha2[1]); + rdev->country_ie_alpha2[0], + rdev->country_ie_alpha2[1]); } else printk(KERN_INFO "cfg80211: Current regulatory " "domain intersected: \n"); @@ -2063,7 +2063,7 @@ static inline void reg_country_ie_process_debug( static int __set_regdom(const struct ieee80211_regdomain *rd) { const struct ieee80211_regdomain *intersected_rd = NULL; - struct cfg80211_registered_device *drv = NULL; + struct cfg80211_registered_device *rdev = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ @@ -2202,11 +2202,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!intersected_rd) return -EINVAL; - drv = wiphy_to_dev(request_wiphy); + rdev = wiphy_to_dev(request_wiphy); - drv->country_ie_alpha2[0] = rd->alpha2[0]; - drv->country_ie_alpha2[1] = rd->alpha2[1]; - drv->env = last_request->country_ie_env; + rdev->country_ie_alpha2[0] = rd->alpha2[0]; + rdev->country_ie_alpha2[1] = rd->alpha2[1]; + rdev->env = last_request->country_ie_env; BUG_ON(intersected_rd == rd); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 472e241..df9173f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -33,15 +33,15 @@ struct cfg80211_conn { static int cfg80211_conn_scan(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_scan_request *request; int n_channels, err; ASSERT_RTNL(); - ASSERT_RDEV_LOCK(drv); + ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); - if (drv->scan_req) + if (rdev->scan_req) return -EBUSY; if (wdev->conn->params.channel) { @@ -87,16 +87,16 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) request->ssids[0].ssid_len = wdev->conn->params.ssid_len; request->ifidx = wdev->netdev->ifindex; - request->wiphy = &drv->wiphy; + request->wiphy = &rdev->wiphy; - drv->scan_req = request; + rdev->scan_req = request; - err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); + err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; - nl80211_send_scan_start(drv, wdev->netdev); + nl80211_send_scan_start(rdev, wdev->netdev); } else { - drv->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } return err; @@ -104,7 +104,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) static int cfg80211_conn_do_work(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; int err; @@ -119,15 +119,15 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_SCAN_AGAIN: return cfg80211_conn_scan(wdev); case CFG80211_CONN_AUTHENTICATE_NEXT: - BUG_ON(!drv->ops->auth); + BUG_ON(!rdev->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return __cfg80211_mlme_auth(drv, wdev->netdev, + return __cfg80211_mlme_auth(rdev, wdev->netdev, params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: - BUG_ON(!drv->ops->assoc); + BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; /* * We could, later, implement roaming here and then actually @@ -135,14 +135,14 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) * that some APs don't like that -- so we'd need to retry * the association. */ - err = __cfg80211_mlme_assoc(drv, wdev->netdev, + err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, NULL, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); if (err) - __cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid, + __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, WLAN_REASON_DEAUTH_LEAVING); return err; @@ -153,15 +153,15 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) void cfg80211_conn_work(struct work_struct *work) { - struct cfg80211_registered_device *drv = + struct cfg80211_registered_device *rdev = container_of(work, struct cfg80211_registered_device, conn_work); struct wireless_dev *wdev; rtnl_lock(); - cfg80211_lock_rdev(drv); - mutex_lock(&drv->devlist_mtx); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &drv->netdev_list, list) { + list_for_each_entry(wdev, &rdev->netdev_list, list) { wdev_lock(wdev); if (!netif_running(wdev->netdev)) { wdev_unlock(wdev); @@ -181,14 +181,14 @@ void cfg80211_conn_work(struct work_struct *work) wdev_unlock(wdev); } - mutex_unlock(&drv->devlist_mtx); - cfg80211_unlock_rdev(drv); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); rtnl_unlock(); } static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bss *bss; u16 capa = WLAN_CAPABILITY_ESS; @@ -209,7 +209,7 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) wdev->conn->params.bssid = wdev->conn->bssid; wdev->conn->params.channel = bss->channel; wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&drv->conn_work); + schedule_work(&rdev->conn_work); cfg80211_put_bss(bss); return true; @@ -218,7 +218,7 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) static void __cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); ASSERT_WDEV_LOCK(wdev); @@ -235,7 +235,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) if (!cfg80211_get_conn_bss(wdev)) { /* not found */ if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) - schedule_work(&drv->conn_work); + schedule_work(&rdev->conn_work); else __cfg80211_connect_result( wdev->netdev, -- cgit v1.1 From 0b20633d966eb08506f8796544aef7a9ab5f3544 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:56:13 +0200 Subject: cfg80211: disallow configuring unsupported interfaces In order to force drivers to advertise their interface types, don't just disallow creating new interfaces with unadvertised types but also disallow setting them UP. Additionally, add some validation on the operations the drivers support. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index b5e2f6d..1a78b3c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -331,8 +331,13 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) struct cfg80211_registered_device *rdev; int alloc_size; - WARN_ON(!ops->add_key && ops->del_key); - WARN_ON(ops->add_key && !ops->del_key); + WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); + WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); + WARN_ON(ops->connect && !ops->disconnect); + WARN_ON(ops->join_ibss && !ops->leave_ibss); + WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); + WARN_ON(ops->add_station && !ops->del_station); + WARN_ON(ops->add_mpath && !ops->del_mpath); alloc_size = sizeof(*rdev) + sizeof_priv; @@ -687,6 +692,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, mutex_destroy(&wdev->mtx); break; case NETDEV_PRE_UP: + if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) + return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); break; -- cgit v1.1 From 487420df79f1d9f5b9de74c9bef378609c475a39 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 08:32:06 +0000 Subject: netlink: remove unused exports I added those myself in commits b4ff4f04 and 84659eb5, but I see no reason now why they should be exported, only generic netlink uses them which cannot be modular. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2936fa3..d7d1b82 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1541,7 +1541,6 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) kfree(old); return err; } -EXPORT_SYMBOL(netlink_change_ngroups); /** * netlink_clear_multicast_users - kick off multicast listeners @@ -1564,7 +1563,6 @@ void netlink_clear_multicast_users(struct sock *ksk, unsigned int group) netlink_table_ungrab(); } -EXPORT_SYMBOL(netlink_clear_multicast_users); void netlink_set_nonroot(int protocol, unsigned int flags) { -- cgit v1.1 From 6c04bb18ddd633b7feac2c8fe2ae0bf61d20ca7a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 09:51:32 +0000 Subject: netlink: use call_rcu for netlink_change_ngroups For the network namespace work in generic netlink I need to be able to call this function under rcu_read_lock(), otherwise the locking becomes a nightmare and more locks would be needed. Instead, just embed a struct rcu_head (actually a struct listeners_rcu_head that also carries the pointer to the memory block) into the listeners memory so we can use call_rcu() instead of synchronising and then freeing. No rcu_barrier() is needed since this code cannot be modular. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d7d1b82..d46da6c 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -83,6 +83,11 @@ struct netlink_sock { struct module *module; }; +struct listeners_rcu_head { + struct rcu_head rcu_head; + void *ptr; +}; + #define NETLINK_KERNEL_SOCKET 0x1 #define NETLINK_RECV_PKTINFO 0x2 #define NETLINK_BROADCAST_SEND_ERROR 0x4 @@ -1453,7 +1458,8 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, if (groups < 32) groups = 32; - listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL); + listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head), + GFP_KERNEL); if (!listeners) goto out_sock_release; @@ -1501,6 +1507,14 @@ netlink_kernel_release(struct sock *sk) EXPORT_SYMBOL(netlink_kernel_release); +static void netlink_free_old_listeners(struct rcu_head *rcu_head) +{ + struct listeners_rcu_head *lrh; + + lrh = container_of(rcu_head, struct listeners_rcu_head, rcu_head); + kfree(lrh->ptr); +} + /** * netlink_change_ngroups - change number of multicast groups * @@ -1516,6 +1530,7 @@ EXPORT_SYMBOL(netlink_kernel_release); int netlink_change_ngroups(struct sock *sk, unsigned int groups) { unsigned long *listeners, *old = NULL; + struct listeners_rcu_head *old_rcu_head; struct netlink_table *tbl = &nl_table[sk->sk_protocol]; int err = 0; @@ -1524,7 +1539,9 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) netlink_table_grab(); if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { - listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC); + listeners = kzalloc(NLGRPSZ(groups) + + sizeof(struct listeners_rcu_head), + GFP_ATOMIC); if (!listeners) { err = -ENOMEM; goto out_ungrab; @@ -1532,13 +1549,22 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) old = tbl->listeners; memcpy(listeners, old, NLGRPSZ(tbl->groups)); rcu_assign_pointer(tbl->listeners, listeners); + /* + * Free the old memory after an RCU grace period so we + * don't leak it. We use call_rcu() here in order to be + * able to call this function from atomic contexts. The + * allocation of this memory will have reserved enough + * space for struct listeners_rcu_head at the end. + */ + old_rcu_head = (void *)(tbl->listeners + + NLGRPLONGS(tbl->groups)); + old_rcu_head->ptr = old; + call_rcu(&old_rcu_head->rcu_head, netlink_free_old_listeners); } tbl->groups = groups; out_ungrab: netlink_table_ungrab(); - synchronize_rcu(); - kfree(old); return err; } -- cgit v1.1 From 11a28d373ed2539a110d56419457e2e7db221ac7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 09:51:33 +0000 Subject: net: make namespace iteration possible under RCU All we need to take care of is using proper RCU list add/del primitives and inserting a synchronize_rcu() at one place to make sure the exit notifiers are run after everybody has stopped iterating the list. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/core/net_namespace.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index b7292a2..5cd0b22 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -127,7 +128,7 @@ static struct net *net_create(void) rv = setup_net(net); if (rv == 0) { rtnl_lock(); - list_add_tail(&net->list, &net_namespace_list); + list_add_tail_rcu(&net->list, &net_namespace_list); rtnl_unlock(); } mutex_unlock(&net_mutex); @@ -156,9 +157,16 @@ static void cleanup_net(struct work_struct *work) /* Don't let anyone else find us. */ rtnl_lock(); - list_del(&net->list); + list_del_rcu(&net->list); rtnl_unlock(); + /* + * Another CPU might be rcu-iterating the list, wait for it. + * This needs to be before calling the exit() notifiers, so + * the rcu_barrier() below isn't sufficient alone. + */ + synchronize_rcu(); + /* Run all of the network namespace exit methods */ list_for_each_entry_reverse(ops, &pernet_list, list) { if (ops->exit) @@ -219,7 +227,7 @@ static int __init net_ns_init(void) panic("Could not setup the initial network namespace"); rtnl_lock(); - list_add_tail(&init_net.list, &net_namespace_list); + list_add_tail_rcu(&init_net.list, &net_namespace_list); rtnl_unlock(); mutex_unlock(&net_mutex); -- cgit v1.1 From 134e63756d5f3d0f7604dfcca847b09d1b14fd66 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 09:51:34 +0000 Subject: genetlink: make netns aware This makes generic netlink network namespace aware. No generic netlink families except for the controller family are made namespace aware, they need to be checked one by one and then set the family->netnsok member to true. A new function genlmsg_multicast_netns() is introduced to allow sending a multicast message in a given namespace, for example when it applies to an object that lives in that namespace, a new function genlmsg_multicast_allns() to send a message to all network namespaces (for objects that do not have an associated netns). The function genlmsg_multicast() is changed to multicast the message in just init_net, which is currently correct for all generic netlink families since they only work in init_net right now. Some will later want to work in all net namespaces because they do not care about the netns at all -- those will have to be converted to use one of the new functions genlmsg_multicast_allns() or genlmsg_multicast_netns() whenever they are made netns aware in some way. After this patch families can easily decide whether or not they should be available in all net namespaces. Many genl families us it for objects not related to networking and should therefore be available in all namespaces, but that will have to be done on a per family basis. Note that this doesn't touch on the checkpoint/restart problem where network namespaces could be used, genl families and multicast groups are numbered globally and I see no easy way of changing that, especially since it must be possible to multicast to all network namespaces for those families that do not care about netns. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/irda/irnetlink.c | 2 +- net/netfilter/ipvs/ip_vs_ctl.c | 2 +- net/netlink/genetlink.c | 186 ++++++++++++++++++++++++++++++++--------- net/tipc/netlink.c | 2 +- net/wireless/nl80211.c | 14 ++-- 5 files changed, 156 insertions(+), 50 deletions(-) (limited to 'net') diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index 8dd7ed7..476b307 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -115,7 +115,7 @@ static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) genlmsg_end(msg, hdr); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); err_out: nlmsg_free(msg); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 7c1333c..2d24d81 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3231,7 +3231,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) } genlmsg_end(msg, reply); - ret = genlmsg_unicast(msg, info->snd_pid); + ret = genlmsg_reply(msg, info); goto out; nla_put_failure: diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index eed4c6a..575c643 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -18,8 +18,6 @@ #include #include -struct sock *genl_sock = NULL; - static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ static inline void genl_lock(void) @@ -175,10 +173,31 @@ int genl_register_mc_group(struct genl_family *family, mc_groups_longs++; } - err = netlink_change_ngroups(genl_sock, - mc_groups_longs * BITS_PER_LONG); - if (err) - goto out; + if (family->netnsok) { + struct net *net; + + rcu_read_lock(); + for_each_net_rcu(net) { + err = netlink_change_ngroups(net->genl_sock, + mc_groups_longs * BITS_PER_LONG); + if (err) { + /* + * No need to roll back, can only fail if + * memory allocation fails and then the + * number of _possible_ groups has been + * increased on some sockets which is ok. + */ + rcu_read_unlock(); + goto out; + } + } + rcu_read_unlock(); + } else { + err = netlink_change_ngroups(init_net.genl_sock, + mc_groups_longs * BITS_PER_LONG); + if (err) + goto out; + } grp->id = id; set_bit(id, mc_groups); @@ -195,8 +214,14 @@ EXPORT_SYMBOL(genl_register_mc_group); static void __genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp) { + struct net *net; BUG_ON(grp->family != family); - netlink_clear_multicast_users(genl_sock, grp->id); + + rcu_read_lock(); + for_each_net_rcu(net) + netlink_clear_multicast_users(net->genl_sock, grp->id); + rcu_read_unlock(); + clear_bit(grp->id, mc_groups); list_del(&grp->list); genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); @@ -467,6 +492,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; struct genl_family *family; + struct net *net = sock_net(skb->sk); struct genl_info info; struct genlmsghdr *hdr = nlmsg_data(nlh); int hdrlen, err; @@ -475,6 +501,10 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (family == NULL) return -ENOENT; + /* this family doesn't exist in this netns */ + if (!family->netnsok && !net_eq(net, &init_net)) + return -ENOENT; + hdrlen = GENL_HDRLEN + family->hdrsize; if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) return -EINVAL; @@ -492,7 +522,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EOPNOTSUPP; genl_unlock(); - err = netlink_dump_start(genl_sock, skb, nlh, + err = netlink_dump_start(net->genl_sock, skb, nlh, ops->dumpit, ops->done); genl_lock(); return err; @@ -514,6 +544,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = family->attrbuf; + genl_info_net_set(&info, net); return ops->doit(skb, &info); } @@ -534,6 +565,7 @@ static struct genl_family genl_ctrl = { .name = "nlctrl", .version = 0x2, .maxattr = CTRL_ATTR_MAX, + .netnsok = true, }; static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, @@ -650,6 +682,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) int i, n = 0; struct genl_family *rt; + struct net *net = sock_net(skb->sk); int chains_to_skip = cb->args[0]; int fams_to_skip = cb->args[1]; @@ -658,6 +691,8 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) continue; n = 0; list_for_each_entry(rt, genl_family_chain(i), family_list) { + if (!rt->netnsok && !net_eq(net, &init_net)) + continue; if (++n < fams_to_skip) continue; if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, @@ -729,6 +764,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) if (info->attrs[CTRL_ATTR_FAMILY_ID]) { u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); res = genl_family_find_byid(id); + err = -ENOENT; } if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { @@ -736,49 +772,61 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); res = genl_family_find_byname(name); + err = -ENOENT; } - if (res == NULL) { - err = -ENOENT; - goto errout; + if (res == NULL) + return err; + + if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { + /* family doesn't exist here */ + return -ENOENT; } msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, CTRL_CMD_NEWFAMILY); - if (IS_ERR(msg)) { - err = PTR_ERR(msg); - goto errout; - } + if (IS_ERR(msg)) + return PTR_ERR(msg); - err = genlmsg_reply(msg, info); -errout: - return err; + return genlmsg_reply(msg, info); } static int genl_ctrl_event(int event, void *data) { struct sk_buff *msg; + struct genl_family *family; + struct genl_multicast_group *grp; - if (genl_sock == NULL) + /* genl is still initialising */ + if (!init_net.genl_sock) return 0; switch (event) { case CTRL_CMD_NEWFAMILY: case CTRL_CMD_DELFAMILY: - msg = ctrl_build_family_msg(data, 0, 0, event); - if (IS_ERR(msg)) - return PTR_ERR(msg); - - genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); + family = data; + msg = ctrl_build_family_msg(family, 0, 0, event); break; case CTRL_CMD_NEWMCAST_GRP: case CTRL_CMD_DELMCAST_GRP: + grp = data; + family = grp->family; msg = ctrl_build_mcgrp_msg(data, 0, 0, event); - if (IS_ERR(msg)) - return PTR_ERR(msg); - - genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); break; + default: + return -EINVAL; + } + + if (IS_ERR(msg)) + return PTR_ERR(msg); + + if (!family->netnsok) { + genlmsg_multicast_netns(&init_net, msg, 0, + GENL_ID_CTRL, GFP_KERNEL); + } else { + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC); + rcu_read_unlock(); } return 0; @@ -795,6 +843,33 @@ static struct genl_multicast_group notify_grp = { .name = "notify", }; +static int __net_init genl_pernet_init(struct net *net) +{ + /* we'll bump the group number right afterwards */ + net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0, + genl_rcv, &genl_mutex, + THIS_MODULE); + + if (!net->genl_sock && net_eq(net, &init_net)) + panic("GENL: Cannot initialize generic netlink\n"); + + if (!net->genl_sock) + return -ENOMEM; + + return 0; +} + +static void __net_exit genl_pernet_exit(struct net *net) +{ + netlink_kernel_release(net->genl_sock); + net->genl_sock = NULL; +} + +static struct pernet_operations genl_pernet_ops = { + .init = genl_pernet_init, + .exit = genl_pernet_exit, +}; + static int __init genl_init(void) { int i, err; @@ -804,36 +879,67 @@ static int __init genl_init(void) err = genl_register_family(&genl_ctrl); if (err < 0) - goto errout; + goto problem; err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops); if (err < 0) - goto errout_register; + goto problem; netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); - /* we'll bump the group number right afterwards */ - genl_sock = netlink_kernel_create(&init_net, NETLINK_GENERIC, 0, - genl_rcv, &genl_mutex, THIS_MODULE); - if (genl_sock == NULL) - panic("GENL: Cannot initialize generic netlink\n"); + err = register_pernet_subsys(&genl_pernet_ops); + if (err) + goto problem; err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); if (err < 0) - goto errout_register; + goto problem; return 0; -errout_register: - genl_unregister_family(&genl_ctrl); -errout: +problem: panic("GENL: Cannot register controller: %d\n", err); } subsys_initcall(genl_init); -EXPORT_SYMBOL(genl_sock); EXPORT_SYMBOL(genl_register_ops); EXPORT_SYMBOL(genl_unregister_ops); EXPORT_SYMBOL(genl_register_family); EXPORT_SYMBOL(genl_unregister_family); + +static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, + gfp_t flags) +{ + struct sk_buff *tmp; + struct net *net, *prev = NULL; + int err; + + for_each_net_rcu(net) { + if (prev) { + tmp = skb_clone(skb, flags); + if (!tmp) { + err = -ENOMEM; + goto error; + } + err = nlmsg_multicast(prev->genl_sock, tmp, + pid, group, flags); + if (err) + goto error; + } + + prev = net; + } + + return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags); + error: + kfree_skb(skb); + return err; +} + +int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group, + gfp_t flags) +{ + return genlmsg_mcast(skb, pid, group, flags); +} +EXPORT_SYMBOL(genlmsg_multicast_allns); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 3c57005..7bda8e3 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -62,7 +62,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; - genlmsg_unicast(rep_buf, NETLINK_CB(skb).pid); + genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).pid); } return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9deb12f..2a04beb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -413,7 +413,7 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) cfg80211_unlock_rdev(dev); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); out_free: nlmsg_free(msg); @@ -739,7 +739,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) dev_put(netdev); cfg80211_unlock_rdev(dev); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); out_free: nlmsg_free(msg); @@ -1030,7 +1030,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) goto nla_put_failure; genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: @@ -1618,7 +1618,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) dev, mac_addr, &sinfo) < 0) goto out_free; - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; out_free: @@ -2087,7 +2087,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) dev, dst, next_hop, &pinfo) < 0) goto out_free; - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; out_free: @@ -2436,7 +2436,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, cur_params.dot11MeshHWMPnetDiameterTraversalTime); nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: @@ -2624,7 +2624,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) nla_nest_end(msg, nl_reg_rules); genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: -- cgit v1.1 From 30ffee8480c13fbcf8ab6c28e31f79dfff683117 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 09:51:35 +0000 Subject: net: move and export get_net_ns_by_pid The function get_net_ns_by_pid(), to get a network namespace from a pid_t, will be required in cfg80211 as well. Therefore, let's move it to net_namespace.c and export it. We can't make it a static inline in the !NETNS case because it needs to verify that the given pid even exists (and return -ESRCH). Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/core/net_namespace.c | 21 +++++++++++++++++++++ net/core/rtnetlink.c | 21 +-------------------- 2 files changed, 22 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 5cd0b22..ddd2cd2 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -201,6 +202,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) } #endif +struct net *get_net_ns_by_pid(pid_t pid) +{ + struct task_struct *tsk; + struct net *net; + + /* Lookup the network namespace */ + net = ERR_PTR(-ESRCH); + rcu_read_lock(); + tsk = find_task_by_vpid(pid); + if (tsk) { + struct nsproxy *nsproxy; + nsproxy = task_nsproxy(tsk); + if (nsproxy) + net = get_net(nsproxy->net_ns); + } + rcu_read_unlock(); + return net; +} +EXPORT_SYMBOL_GPL(get_net_ns_by_pid); + static int __init net_ns_init(void) { struct net_generic *ng; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d78030f..b44775f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -52,6 +51,7 @@ #include #include #include +#include struct rtnl_link { @@ -725,25 +725,6 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { [IFLA_INFO_DATA] = { .type = NLA_NESTED }, }; -static struct net *get_net_ns_by_pid(pid_t pid) -{ - struct task_struct *tsk; - struct net *net; - - /* Lookup the network namespace */ - net = ERR_PTR(-ESRCH); - rcu_read_lock(); - tsk = find_task_by_vpid(pid); - if (tsk) { - struct nsproxy *nsproxy; - nsproxy = task_nsproxy(tsk); - if (nsproxy) - net = get_net(nsproxy->net_ns); - } - rcu_read_unlock(); - return net; -} - static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[]) { if (dev) { -- cgit v1.1 From d7ca4cc01fd154f2da30ae6dae160fa5800af758 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 9 Jul 2009 08:09:47 +0000 Subject: udpv4: Handle large incoming UDP/IPv4 packets and support software UFO. - validate and forward GSO UDP/IPv4 packets from untrusted sources. - do software UFO if the outgoing device doesn't support UFO. Signed-off-by: Sridhar Samudrala Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 12 ++++++++++- net/ipv4/udp.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 566ea6c..197d024 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1187,6 +1187,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) int proto; int ihl; int id; + unsigned int offset = 0; if (!(features & NETIF_F_V4_CSUM)) features &= ~NETIF_F_SG; @@ -1229,7 +1230,14 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) skb = segs; do { iph = ip_hdr(skb); - iph->id = htons(id++); + if (proto == IPPROTO_UDP) { + iph->id = htons(id); + iph->frag_off = htons(offset >> 3); + if (skb->next != NULL) + iph->frag_off |= htons(IP_MF); + offset += (skb->len - skb->mac_len - iph->ihl * 4); + } else + iph->id = htons(id++); iph->tot_len = htons(skb->len - skb->mac_len); iph->check = 0; iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); @@ -1425,6 +1433,8 @@ static struct net_protocol tcp_protocol = { static struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = udp_err, + .gso_send_check = udp4_ufo_send_check, + .gso_segment = udp4_ufo_fragment, .no_policy = 1, .netns_ok = 1, }; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 80e3812..7bc2d08 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1816,6 +1816,67 @@ void __init udp_init(void) sysctl_udp_wmem_min = SK_MEM_QUANTUM; } +int udp4_ufo_send_check(struct sk_buff *skb) +{ + const struct iphdr *iph; + struct udphdr *uh; + + if (!pskb_may_pull(skb, sizeof(*uh))) + return -EINVAL; + + iph = ip_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + return 0; +} + +struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + int offset; + __wsum csum; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot + * do checksum of UDP packets sent as multiple IP fragments. + */ + offset = skb->csum_start - skb_headroom(skb); + csum = skb_checksum(skb, offset, skb->len- offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + /* Fragment the skb. IP headers of the fragments are updated in + * inet_gso_segment() + */ + segs = skb_segment(skb, features); +out: + return segs; +} + EXPORT_SYMBOL(udp_disconnect); EXPORT_SYMBOL(udp_ioctl); EXPORT_SYMBOL(udp_prot); -- cgit v1.1 From 493c6be3fedfe24aa676949b237b9b104d911abf Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 9 Jul 2009 08:09:54 +0000 Subject: udpv6: Fix HW checksum support for outgoing UFO packets - add HW checksum support for outgoing large UDP/IPv6 packets destined for a UFO enabled device. Signed-off-by: Sridhar Samudrala Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv6/udp.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 33b59bd..f31b1b9 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -638,6 +638,47 @@ static void udp_v6_flush_pending_frames(struct sock *sk) } } +/** + * udp6_hwcsum_outgoing - handle outgoing HW checksumming + * @sk: socket we are sending on + * @skb: sk_buff containing the filled-in UDP header + * (checksum field must be zeroed out) + */ +static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, + const struct in6_addr *saddr, + const struct in6_addr *daddr, int len) +{ + unsigned int offset; + struct udphdr *uh = udp_hdr(skb); + __wsum csum = 0; + + if (skb_queue_len(&sk->sk_write_queue) == 1) { + /* Only one fragment on the socket. */ + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + uh->check = ~csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, 0); + } else { + /* + * HW-checksum won't work as there are two or more + * fragments on the socket so that all csums of sk_buffs + * should be together + */ + offset = skb_transport_offset(skb); + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + + skb->ip_summed = CHECKSUM_NONE; + + skb_queue_walk(&sk->sk_write_queue, skb) { + csum = csum_add(csum, skb->csum); + } + + uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, + csum); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + } +} + /* * Sending */ @@ -668,7 +709,11 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (is_udplite) csum = udplite_csum_outgoing(sk, skb); - else + else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ + udp6_hwcsum_outgoing(sk, skb, &fl->fl6_src, &fl->fl6_dst, + up->len); + goto send; + } else csum = udp_csum_outgoing(sk, skb); /* add protocol-dependent pseudo-header */ @@ -677,6 +722,7 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (uh->check == 0) uh->check = CSUM_MANGLED_0; +send: err = ip6_push_pending_frames(sk); out: up->len = 0; -- cgit v1.1 From c31d5326902cebffcd83b1aede67a0e0ac923090 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 9 Jul 2009 08:09:58 +0000 Subject: udpv6: Fix gso_size setting in ip6_ufo_append_data - fix gso_size setting for ipv6 fragment to be a multiple of 8 bytes. Signed-off-by: Sridhar Samudrala Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7c76e3d..1c6f0fc 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1087,9 +1087,11 @@ static inline int ip6_ufo_append_data(struct sock *sk, if (!err) { struct frag_hdr fhdr; - /* specify the length of each IP datagram fragment*/ - skb_shinfo(skb)->gso_size = mtu - fragheaderlen - - sizeof(struct frag_hdr); + /* Specify the length of each IPv6 datagram fragment. + * It has to be a multiple of 8. + */ + skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - + sizeof(struct frag_hdr)) & ~7; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; ipv6_select_ident(skb, &fhdr); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; -- cgit v1.1 From 7ea2f2c5a66e4e9a8d96296ac47ad895c467ee1d Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 9 Jul 2009 08:10:01 +0000 Subject: udpv6: Remove unused skb argument of ipv6_select_ident() - move ipv6_select_ident() inline function to ipv6.h and remove the unused skb argument Signed-off-by: Sridhar Samudrala Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 1c6f0fc..dd1a980 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -57,18 +57,6 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); -static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) -{ - static u32 ipv6_fragmentation_id = 1; - static DEFINE_SPINLOCK(ip6_id_lock); - - spin_lock_bh(&ip6_id_lock); - fhdr->identification = htonl(ipv6_fragmentation_id); - if (++ipv6_fragmentation_id == 0) - ipv6_fragmentation_id = 1; - spin_unlock_bh(&ip6_id_lock); -} - int __ip6_local_out(struct sk_buff *skb) { int len; @@ -706,7 +694,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); - ipv6_select_ident(skb, fh); + ipv6_select_ident(fh); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); @@ -844,7 +832,7 @@ slow_path: fh->nexthdr = nexthdr; fh->reserved = 0; if (!frag_id) { - ipv6_select_ident(skb, fh); + ipv6_select_ident(fh); frag_id = fh->identification; } else fh->identification = frag_id; @@ -1093,7 +1081,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - sizeof(struct frag_hdr)) & ~7; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - ipv6_select_ident(skb, &fhdr); + ipv6_select_ident(&fhdr); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; __skb_queue_tail(&sk->sk_write_queue, skb); -- cgit v1.1 From ba73542585a4a3c8a708f502e62e6e63dd74b66c Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 9 Jul 2009 08:10:04 +0000 Subject: udpv6: Handle large incoming UDP/IPv6 packets and support software UFO - validate and forward GSO UDP/IPv6 packets from untrusted sources. - do software UFO if the outgoing device doesn't support UFO. Signed-off-by: Sridhar Samudrala Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/ipv6/af_inet6.c | 20 ++++++++++-- net/ipv6/udp.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index caa0278..bf85d5f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -772,6 +772,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) struct sk_buff *segs = ERR_PTR(-EINVAL); struct ipv6hdr *ipv6h; struct inet6_protocol *ops; + int proto; + struct frag_hdr *fptr; + unsigned int unfrag_ip6hlen; + u8 *prevhdr; + int offset = 0; if (!(features & NETIF_F_V6_CSUM)) features &= ~NETIF_F_SG; @@ -791,10 +796,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) __skb_pull(skb, sizeof(*ipv6h)); segs = ERR_PTR(-EPROTONOSUPPORT); + proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); rcu_read_lock(); - ops = rcu_dereference(inet6_protos[ - ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); - + ops = rcu_dereference(inet6_protos[proto]); if (likely(ops && ops->gso_segment)) { skb_reset_transport_header(skb); segs = ops->gso_segment(skb, features); @@ -808,6 +812,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) ipv6h = ipv6_hdr(skb); ipv6h->payload_len = htons(skb->len - skb->mac_len - sizeof(*ipv6h)); + if (proto == IPPROTO_UDP) { + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + fptr = (struct frag_hdr *)(skb_network_header(skb) + + unfrag_ip6hlen); + fptr->frag_off = htons(offset); + if (skb->next != NULL) + fptr->frag_off |= htons(IP6_MF); + offset += (ntohs(ipv6h->payload_len) - + sizeof(struct frag_hdr)); + } } out: diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f31b1b9..d79fa67 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1078,9 +1078,102 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif +static int udp6_ufo_send_check(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct udphdr *uh; + + if (!pskb_may_pull(skb, sizeof(*uh))) + return -EINVAL; + + ipv6h = ipv6_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + return 0; +} + +static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + unsigned int unfrag_ip6hlen, unfrag_len; + struct frag_hdr *fptr; + u8 *mac_start, *prevhdr; + u8 nexthdr; + u8 frag_hdr_sz = sizeof(struct frag_hdr); + int offset; + __wsum csum; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot + * do checksum of UDP packets sent as multiple IP fragments. + */ + offset = skb->csum_start - skb_headroom(skb); + csum = skb_checksum(skb, offset, skb->len- offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + /* Check if there is enough headroom to insert fragment header. */ + if ((skb_headroom(skb) < frag_hdr_sz) && + pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) + goto out; + + /* Find the unfragmentable header and shift it left by frag_hdr_sz + * bytes to insert fragment header. + */ + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + nexthdr = *prevhdr; + *prevhdr = NEXTHDR_FRAGMENT; + unfrag_len = skb_network_header(skb) - skb_mac_header(skb) + + unfrag_ip6hlen; + mac_start = skb_mac_header(skb); + memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len); + + skb->mac_header -= frag_hdr_sz; + skb->network_header -= frag_hdr_sz; + + fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); + fptr->nexthdr = nexthdr; + fptr->reserved = 0; + ipv6_select_ident(fptr); + + /* Fragment the skb. ipv6 header and the remaining fields of the + * fragment header are updated in ipv6_gso_segment() + */ + segs = skb_segment(skb, features); + +out: + return segs; +} + static struct inet6_protocol udpv6_protocol = { .handler = udpv6_rcv, .err_handler = udpv6_err, + .gso_send_check = udp6_ufo_send_check, + .gso_segment = udp6_ufo_fragment, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; -- cgit v1.1 From df5ede82588487db1894933af217e4aa710978d5 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Wed, 8 Jul 2009 18:06:47 +0000 Subject: net: remove redundant sched/ in net/Makefile Remove redundant sched/ in net/Makefile. sched/ is contained in previous: obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/, so the later obj-$(CONFIG_NET_SCHED) += sched/ isn't necessary. Signed-off-by: Changli Gao ---- Makefile | 1 - 1 file changed, 1 deletion(-) Signed-off-by: David S. Miller --- net/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/Makefile b/net/Makefile index ba324ae..1542e72 100644 --- a/net/Makefile +++ b/net/Makefile @@ -24,7 +24,6 @@ obj-y += ipv6/ endif obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ -obj-$(CONFIG_NET_SCHED) += sched/ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ -- cgit v1.1 From 97fd5bc7f2e442482a7a6cc4bc2a286cbb5f4754 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 13 Jul 2009 11:17:49 -0700 Subject: net: Rename lookup_neigh_params function Rename lookup_neigh_params to lookup_neigh_parms as the struct is named neigh_parms and all other functions dealing with the struct carry neigh_parms in their names. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- net/core/neighbour.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 163b4f5..c6f9ad8 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1316,7 +1316,7 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, } EXPORT_SYMBOL(pneigh_enqueue); -static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl, +static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, struct net *net, int ifindex) { struct neigh_parms *p; @@ -1337,7 +1337,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct net *net = dev_net(dev); const struct net_device_ops *ops = dev->netdev_ops; - ref = lookup_neigh_params(tbl, net, 0); + ref = lookup_neigh_parms(tbl, net, 0); if (!ref) return NULL; @@ -1906,7 +1906,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (tbp[NDTPA_IFINDEX]) ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); - p = lookup_neigh_params(tbl, net, ifindex); + p = lookup_neigh_parms(tbl, net, ifindex); if (p == NULL) { err = -ENOENT; goto errout_tbl_lock; -- cgit v1.1 From b333b3d22822cf9b295990866798e9239c9dee72 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 24 Jun 2009 01:34:48 +0000 Subject: wireless extensions: make netns aware This makes wireless extensions netns aware. The tasklet sending the events is converted to a work struct so that we can rtnl_lock() in it. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/wireless/wext.c | 61 +++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 425f7d5..db8351a 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1257,48 +1257,48 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, } #endif -/************************* EVENT PROCESSING *************************/ -/* - * Process events generated by the wireless layer or the driver. - * Most often, the event will be propagated through rtnetlink - */ +static int __net_init wext_pernet_init(struct net *net) +{ + skb_queue_head_init(&net->wext_nlevents); + return 0; +} -/* ---------------------------------------------------------------- */ -/* - * Locking... - * ---------- - * - * Thanks to Herbert Xu for fixing - * the locking issue in here and implementing this code ! - * - * The issue : wireless_send_event() is often called in interrupt context, - * while the Netlink layer can never be called in interrupt context. - * The fully formed RtNetlink events are queued, and then a tasklet is run - * to feed those to Netlink. - * The skb_queue is interrupt safe, and its lock is not held while calling - * Netlink, so there is no possibility of dealock. - * Jean II - */ +static void __net_exit wext_pernet_exit(struct net *net) +{ + skb_queue_purge(&net->wext_nlevents); +} -static struct sk_buff_head wireless_nlevent_queue; +static struct pernet_operations wext_pernet_ops = { + .init = wext_pernet_init, + .exit = wext_pernet_exit, +}; static int __init wireless_nlevent_init(void) { - skb_queue_head_init(&wireless_nlevent_queue); + return register_pernet_subsys(&wext_pernet_ops); return 0; } subsys_initcall(wireless_nlevent_init); -static void wireless_nlevent_process(unsigned long data) +/* Process events generated by the wireless layer or the driver. */ +static void wireless_nlevent_process(struct work_struct *work) { struct sk_buff *skb; + struct net *net; + + rtnl_lock(); - while ((skb = skb_dequeue(&wireless_nlevent_queue))) - rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + for_each_net(net) { + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, + GFP_KERNEL); + } + + rtnl_unlock(); } -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); +static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); /* ---------------------------------------------------------------- */ /* @@ -1348,9 +1348,6 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) struct sk_buff *skb; int err; - if (!net_eq(dev_net(dev), &init_net)) - return; - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!skb) return; @@ -1363,8 +1360,8 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) } NETLINK_CB(skb).dst_group = RTNLGRP_LINK; - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); + skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); + schedule_work(&wireless_nlevent_work); } /* ---------------------------------------------------------------- */ -- cgit v1.1 From 4f45b2cd4e78b5e49d7d41548345b879d3fdfeae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 24 Jun 2009 01:34:49 +0000 Subject: wext: optimise, comment and fix event sending The current function for sending events first allocates the event stream buffer, and then an skb to copy the event stream into. This can be done in one go. Also, the current function leaks kernel data to userspace in a 4 uninitialised bytes, initialise those explicitly. Finally also add a few useful comments, as opposed to the current comments. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/wireless/wext.c | 114 ++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index db8351a..ee35e64 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1300,22 +1300,15 @@ static void wireless_nlevent_process(struct work_struct *work) static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); -/* ---------------------------------------------------------------- */ -/* - * Fill a rtnetlink message with our event data. - * Note that we propage only the specified event and don't dump the - * current wireless config. Dumping the wireless config is far too - * expensive (for each parameter, the driver need to query the hardware). - */ -static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, - int type, char *event, int event_len) +static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, + struct sk_buff *skb) { struct ifinfomsg *r; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0); - if (nlh == NULL) - return -EMSGSIZE; + nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); + if (!nlh) + return NULL; r = nlmsg_data(nlh); r->ifi_family = AF_UNSPEC; @@ -1326,45 +1319,14 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, r->ifi_change = 0; /* Wireless changes don't affect those flags */ NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); - /* Add the wireless events in the netlink packet */ - NLA_PUT(skb, IFLA_WIRELESS, event_len, event); - - return nlmsg_end(skb, nlh); -nla_put_failure: + return nlh; + nla_put_failure: nlmsg_cancel(skb, nlh); - return -EMSGSIZE; + return NULL; } -/* ---------------------------------------------------------------- */ -/* - * Create and broadcast and send it on the standard rtnetlink socket - * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c - * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field - * within a RTM_NEWLINK event. - */ -static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) -{ - struct sk_buff *skb; - int err; - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) - return; - err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len); - if (err < 0) { - WARN_ON(err == -EMSGSIZE); - kfree_skb(skb); - return; - } - - NETLINK_CB(skb).dst_group = RTNLGRP_LINK; - skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); - schedule_work(&wireless_nlevent_work); -} - -/* ---------------------------------------------------------------- */ /* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. @@ -1383,6 +1345,9 @@ void wireless_send_event(struct net_device * dev, int wrqu_off = 0; /* Offset in wrqu */ /* Don't "optimise" the following variable, it will crash */ unsigned cmd_index; /* *MUST* be unsigned */ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct nlattr *nla; /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { @@ -1430,25 +1395,60 @@ void wireless_send_event(struct net_device * dev, hdr_len = event_type_size[descr->header_type]; event_len = hdr_len + extra_len; - /* Create temporary buffer to hold the event */ - event = kmalloc(event_len, GFP_ATOMIC); - if (event == NULL) + /* + * The problem for 64/32 bit. + * + * On 64-bit, a regular event is laid out as follows: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | wrqu data ... (with the correct size) | + * + * This padding exists because we manipulate event->u, + * and 'event' is not packed. + * + * An iw_point event is laid out like this instead: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | iwpnt.len | iwpnt.flg | p a d d i n g | + * | extra data ... + * + * The second padding exists because struct iw_point is extended, + * but this depends on the platform... + * + * On 32-bit, all the padding shouldn't be there. + */ + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!skb) + return; + + /* Send via the RtNetlink event channel */ + nlh = rtnetlink_ifinfo_prep(dev, skb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + return; + } + + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(skb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); return; + } + event = nla_data(nla); - /* Fill event */ + /* Fill event - first clear to avoid data leaking */ + memset(event, 0, hdr_len); event->len = event_len; event->cmd = cmd; memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); - if (extra) + if (extra_len) memcpy(((char *) event) + hdr_len, extra, extra_len); - /* Send via the RtNetlink event channel */ - rtmsg_iwinfo(dev, (char *) event, event_len); - - /* Cleanup */ - kfree(event); + nlmsg_end(skb, nlh); - return; /* Always success, I guess ;-) */ + skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); + schedule_work(&wireless_nlevent_work); } EXPORT_SYMBOL(wireless_send_event); -- cgit v1.1 From 1dacc76d0014a034b8aca14237c127d7c19d7726 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 11:26:02 +0000 Subject: net/compat/wext: send different messages to compat tasks Wireless extensions have the unfortunate problem that events are multicast netlink messages, and are not independent of pointer size. Thus, currently 32-bit tasks on 64-bit platforms cannot properly receive events and fail with all kinds of strange problems, for instance wpa_supplicant never notices disassociations, due to the way the 64-bit event looks (to a 32-bit process), the fact that the address is all zeroes is lost, it thinks instead it is 00:00:00:00:01:00. The same problem existed with the ioctls, until David Miller fixed those some time ago in an heroic effort. A different problem caused by this is that we cannot send the ASSOCREQIE/ASSOCRESPIE events because sending them causes a 32-bit wpa_supplicant on a 64-bit system to overwrite its internal information, which is worse than it not getting the information at all -- so we currently resort to sending a custom string event that it then parses. This, however, has a severe size limitation we are frequently hitting with modern access points; this limitation would can be lifted after this patch by sending the correct binary, not custom, event. A similar problem apparently happens for some other netlink users on x86_64 with 32-bit tasks due to the alignment for 64-bit quantities. In order to fix these problems, I have implemented a way to send compat messages to tasks. When sending an event, we send the non-compat event data together with a compat event data in skb_shinfo(main_skb)->frag_list. Then, when the event is read from the socket, the netlink code makes sure to pass out only the skb that is compatible with the task. This approach was suggested by David Miller, my original approach required always sending two skbs but that had various small problems. To determine whether compat is needed or not, I have used the MSG_CMSG_COMPAT flag, and adjusted the call path for recv and recvfrom to include it, even if those calls do not have a cmsg parameter. I have not solved one small part of the problem, and I don't think it is necessary to: if a 32-bit application uses read() rather than any form of recvmsg() it will still get the wrong (64-bit) event. However, neither do applications actually do this, nor would it be a regression. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- net/Kconfig | 20 +++++++++++++ net/compat.c | 17 +++++++++-- net/netlink/af_netlink.c | 36 +++++++++++++++++++++- net/wireless/wext.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/Kconfig b/net/Kconfig index 7051b97..041c35e 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -23,6 +23,26 @@ menuconfig NET if NET +config WANT_COMPAT_NETLINK_MESSAGES + bool + help + This option can be selected by other options that need compat + netlink messages. + +config COMPAT_NETLINK_MESSAGES + def_bool y + depends on COMPAT + depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES + help + This option makes it possible to send different netlink messages + to tasks depending on whether the task is a compat task or not. To + achieve this, you need to set skb_shinfo(skb)->frag_list to the + compat skb before sending the skb, the netlink code will sort out + which message to actually pass to the task. + + Newly written code should NEVER need this option but do + compat-independent messages instead! + menu "Networking options" source "net/packet/Kconfig" diff --git a/net/compat.c b/net/compat.c index 8d73905..12728b1 100644 --- a/net/compat.c +++ b/net/compat.c @@ -743,6 +743,18 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } +asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags) +{ + return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT); +} + +asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len, + unsigned flags, struct sockaddr __user *addr, + int __user *addrlen) +{ + return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); +} + asmlinkage long compat_sys_socketcall(int call, u32 __user *args) { int ret; @@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]); break; case SYS_RECV: - ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); + ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]); break; case SYS_RECVFROM: - ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); + ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], + compat_ptr(a[4]), compat_ptr(a[5])); break; case SYS_SHUTDOWN: ret = sys_shutdown(a0,a1); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d46da6c..da3163d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1361,7 +1361,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, struct netlink_sock *nlk = nlk_sk(sk); int noblock = flags&MSG_DONTWAIT; size_t copied; - struct sk_buff *skb; + struct sk_buff *skb, *frag __maybe_unused = NULL; int err; if (flags&MSG_OOB) @@ -1373,6 +1373,35 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, if (skb == NULL) goto out; +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES + if (unlikely(skb_shinfo(skb)->frag_list)) { + bool need_compat = !!(flags & MSG_CMSG_COMPAT); + + /* + * If this skb has a frag_list, then here that means that + * we will have to use the frag_list skb for compat tasks + * and the regular skb for non-compat tasks. + * + * The skb might (and likely will) be cloned, so we can't + * just reset frag_list and go on with things -- we need to + * keep that. For the compat case that's easy -- simply get + * a reference to the compat skb and free the regular one + * including the frag. For the non-compat case, we need to + * avoid sending the frag to the user -- so assign NULL but + * restore it below before freeing the skb. + */ + if (need_compat) { + struct sk_buff *compskb = skb_shinfo(skb)->frag_list; + skb_get(compskb); + kfree_skb(skb); + skb = compskb; + } else { + frag = skb_shinfo(skb)->frag_list; + skb_shinfo(skb)->frag_list = NULL; + } + } +#endif + msg->msg_namelen = 0; copied = skb->len; @@ -1403,6 +1432,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, siocb->scm->creds = *NETLINK_CREDS(skb); if (flags & MSG_TRUNC) copied = skb->len; + +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES + skb_shinfo(skb)->frag_list = frag; +#endif + skb_free_datagram(sk, skb); if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) diff --git a/net/wireless/wext.c b/net/wireless/wext.c index ee35e64..3fe3c2c 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -417,6 +417,21 @@ static const int event_type_size[] = { IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ }; +#ifdef CONFIG_COMPAT +static const int compat_event_type_size[] = { + IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ + IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; +#endif /************************ COMMON SUBROUTINES ************************/ /* @@ -1348,6 +1363,22 @@ void wireless_send_event(struct net_device * dev, struct sk_buff *skb; struct nlmsghdr *nlh; struct nlattr *nla; +#ifdef CONFIG_COMPAT + struct __compat_iw_event *compat_event; + struct compat_iw_point compat_wrqu; + struct sk_buff *compskb; +#endif + + /* + * Nothing in the kernel sends scan events with data, be safe. + * This is necessary because we cannot fix up scan event data + * for compat, due to being contained in 'extra', but normally + * applications are required to retrieve the scan data anyway + * and no data is included in the event, this codifies that + * practice. + */ + if (WARN_ON(cmd == SIOCGIWSCAN && extra)) + extra = NULL; /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { @@ -1446,7 +1477,54 @@ void wireless_send_event(struct net_device * dev, memcpy(((char *) event) + hdr_len, extra, extra_len); nlmsg_end(skb, nlh); +#ifdef CONFIG_COMPAT + hdr_len = compat_event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!compskb) { + kfree_skb(skb); + return; + } + + /* Send via the RtNetlink event channel */ + nlh = rtnetlink_ifinfo_prep(dev, compskb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } + + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } + compat_event = nla_data(nla); + + compat_event->len = event_len; + compat_event->cmd = cmd; + if (descr->header_type == IW_HEADER_TYPE_POINT) { + compat_wrqu.length = wrqu->data.length; + compat_wrqu.flags = wrqu->data.flags; + memcpy(&compat_event->pointer, + ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, + hdr_len - IW_EV_COMPAT_LCP_LEN); + if (extra_len) + memcpy(((char *) compat_event) + hdr_len, + extra, extra_len); + } else { + /* extra_len must be zero, so no if (extra) needed */ + memcpy(&compat_event->pointer, wrqu, + hdr_len - IW_EV_COMPAT_LCP_LEN); + } + + nlmsg_end(compskb, nlh); + + skb_shinfo(skb)->frag_list = compskb; +#endif skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); schedule_work(&wireless_nlevent_work); } -- cgit v1.1 From c482c568577a2b31716e1019f2868bda7cf5629c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 17 Jul 2009 00:26:32 +0000 Subject: udp: cleanups Pure style cleanups. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/udp.c | 87 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 7bc2d08..29ebb0d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -110,11 +110,12 @@ struct udp_table udp_table; EXPORT_SYMBOL(udp_table); int sysctl_udp_mem[3] __read_mostly; -int sysctl_udp_rmem_min __read_mostly; -int sysctl_udp_wmem_min __read_mostly; - EXPORT_SYMBOL(sysctl_udp_mem); + +int sysctl_udp_rmem_min __read_mostly; EXPORT_SYMBOL(sysctl_udp_rmem_min); + +int sysctl_udp_wmem_min __read_mostly; EXPORT_SYMBOL(sysctl_udp_wmem_min); atomic_t udp_memory_allocated; @@ -158,7 +159,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, */ int udp_lib_get_port(struct sock *sk, unsigned short snum, int (*saddr_comp)(const struct sock *sk1, - const struct sock *sk2 ) ) + const struct sock *sk2)) { struct udp_hslot *hslot; struct udp_table *udptable = sk->sk_prot->h.udp_table; @@ -221,14 +222,15 @@ fail_unlock: fail: return error; } +EXPORT_SYMBOL(udp_lib_get_port); static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) { struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); - return ( !ipv6_only_sock(sk2) && - (!inet1->rcv_saddr || !inet2->rcv_saddr || - inet1->rcv_saddr == inet2->rcv_saddr )); + return (!ipv6_only_sock(sk2) && + (!inet1->rcv_saddr || !inet2->rcv_saddr || + inet1->rcv_saddr == inet2->rcv_saddr)); } int udp_v4_get_port(struct sock *sk, unsigned short snum) @@ -383,8 +385,8 @@ found: void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) { struct inet_sock *inet; - struct iphdr *iph = (struct iphdr*)skb->data; - struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2)); + struct iphdr *iph = (struct iphdr *)skb->data; + struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2)); const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; struct sock *sk; @@ -439,7 +441,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) if (!harderr || sk->sk_state != TCP_ESTABLISHED) goto out; } else { - ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1)); + ip_icmp_error(sk, skb, err, uh->dest, info, (u8 *)(uh+1)); } sk->sk_err = err; sk->sk_error_report(sk); @@ -474,7 +476,7 @@ EXPORT_SYMBOL(udp_flush_pending_frames); * (checksum field must be zeroed out) */ static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, - __be32 src, __be32 dst, int len ) + __be32 src, __be32 dst, int len) { unsigned int offset; struct udphdr *uh = udp_hdr(skb); @@ -545,7 +547,7 @@ static int udp_push_pending_frames(struct sock *sk) } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ - udp4_hwcsum_outgoing(sk, skb, fl->fl4_src,fl->fl4_dst, up->len); + udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len); goto send; } else /* `normal' UDP */ @@ -553,7 +555,7 @@ static int udp_push_pending_frames(struct sock *sk) /* add protocol-dependent pseudo-header */ uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len, - sk->sk_protocol, csum ); + sk->sk_protocol, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; @@ -592,7 +594,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, * Check the flags. */ - if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */ + if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */ return -EOPNOTSUPP; ipc.opt = NULL; @@ -619,7 +621,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, * Get and verify the address. */ if (msg->msg_name) { - struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name; + struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name; if (msg->msg_namelen < sizeof(*usin)) return -EINVAL; if (usin->sin_family != AF_INET) { @@ -684,7 +686,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } if (connected) - rt = (struct rtable*)sk_dst_check(sk, 0); + rt = (struct rtable *)sk_dst_check(sk, 0); if (rt == NULL) { struct flowi fl = { .oif = ipc.oif, @@ -782,6 +784,7 @@ do_confirm: err = 0; goto out; } +EXPORT_SYMBOL(udp_sendmsg); int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags) @@ -871,6 +874,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) return 0; } +EXPORT_SYMBOL(udp_ioctl); /* * This should be easy, if there is something there we @@ -892,7 +896,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, * Check any passed addresses */ if (addr_len) - *addr_len=sizeof(*sin); + *addr_len = sizeof(*sin); if (flags & MSG_ERRQUEUE) return ip_recv_error(sk, msg, len); @@ -923,9 +927,11 @@ try_again: if (skb_csum_unnecessary(skb)) err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov, copied ); + msg->msg_iov, copied); else { - err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov); + err = skb_copy_and_csum_datagram_iovec(skb, + sizeof(struct udphdr), + msg->msg_iov); if (err == -EINVAL) goto csum_copy_err; @@ -941,8 +947,7 @@ try_again: sock_recv_timestamp(msg, sk, skb); /* Copy the address. */ - if (sin) - { + if (sin) { sin->sin_family = AF_INET; sin->sin_port = udp_hdr(skb)->source; sin->sin_addr.s_addr = ip_hdr(skb)->saddr; @@ -995,6 +1000,7 @@ int udp_disconnect(struct sock *sk, int flags) sk_dst_reset(sk); return 0; } +EXPORT_SYMBOL(udp_disconnect); void udp_lib_unhash(struct sock *sk) { @@ -1044,7 +1050,7 @@ drop: * Note that in the success and error cases, the skb is assumed to * have either been requeued or freed. */ -int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) +int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct udp_sock *up = udp_sk(sk); int rc; @@ -1214,7 +1220,7 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, if (uh->check == 0) { skb->ip_summed = CHECKSUM_UNNECESSARY; } else if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, proto, skb->csum)) skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -1355,7 +1361,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, int err = 0; int is_udplite = IS_UDPLITE(sk); - if (optlenf_flags & O_NONBLOCK) && - !(sk->sk_shutdown & RCV_SHUTDOWN)){ + if ((mask & POLLRDNORM) && + !(file->f_flags & O_NONBLOCK) && + !(sk->sk_shutdown & RCV_SHUTDOWN)) { struct sk_buff_head *rcvq = &sk->sk_receive_queue; struct sk_buff *skb; @@ -1552,6 +1560,7 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) return mask; } +EXPORT_SYMBOL(udp_poll); struct proto udp_prot = { .name = "UDP", @@ -1582,6 +1591,7 @@ struct proto udp_prot = { .compat_getsockopt = compat_udp_getsockopt, #endif }; +EXPORT_SYMBOL(udp_prot); /* ------------------------------------------------------------------------ */ #ifdef CONFIG_PROC_FS @@ -1703,11 +1713,13 @@ int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo) rc = -ENOMEM; return rc; } +EXPORT_SYMBOL(udp_proc_register); void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo) { proc_net_remove(net, afinfo->name); } +EXPORT_SYMBOL(udp_proc_unregister); /* ------------------------------------------------------------------------ */ static void udp4_format_sock(struct sock *sp, struct seq_file *f, @@ -1741,7 +1753,7 @@ int udp4_seq_show(struct seq_file *seq, void *v) int len; udp4_format_sock(v, seq, state->bucket, &len); - seq_printf(seq, "%*s\n", 127 - len ,""); + seq_printf(seq, "%*s\n", 127 - len, ""); } return 0; } @@ -1864,7 +1876,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features) * do checksum of UDP packets sent as multiple IP fragments. */ offset = skb->csum_start - skb_headroom(skb); - csum = skb_checksum(skb, offset, skb->len- offset, 0); + csum = skb_checksum(skb, offset, skb->len - offset, 0); offset += skb->csum_offset; *(__sum16 *)(skb->data + offset) = csum_fold(csum); skb->ip_summed = CHECKSUM_NONE; @@ -1877,16 +1889,3 @@ out: return segs; } -EXPORT_SYMBOL(udp_disconnect); -EXPORT_SYMBOL(udp_ioctl); -EXPORT_SYMBOL(udp_prot); -EXPORT_SYMBOL(udp_sendmsg); -EXPORT_SYMBOL(udp_lib_getsockopt); -EXPORT_SYMBOL(udp_lib_setsockopt); -EXPORT_SYMBOL(udp_poll); -EXPORT_SYMBOL(udp_lib_get_port); - -#ifdef CONFIG_PROC_FS -EXPORT_SYMBOL(udp_proc_register); -EXPORT_SYMBOL(udp_proc_unregister); -#endif -- cgit v1.1 From 2e477c9bd2bb6a1606e498adb53ba913378ecdf2 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 20 Jul 2009 07:35:37 -0700 Subject: vlan: Propagate physical MTU changes When the physical MTU changes we want to ensure that all existing VLAN device MTUs do not exceed the new underlying MTU. This patch adds that propagation. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/8021q/vlan.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index fe64908..6d37b7e 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -468,6 +468,19 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, } break; + case NETDEV_CHANGEMTU: + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + vlandev = vlan_group_get_device(grp, i); + if (!vlandev) + continue; + + if (vlandev->mtu <= dev->mtu) + continue; + + dev_set_mtu(vlandev, dev->mtu); + } + break; + case NETDEV_FEAT_CHANGE: /* Propagate device features to underlying device */ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { -- cgit v1.1 From c3059477fce2d956a0bb3e04357324780c5d8eeb Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Tue, 14 Jul 2009 08:33:08 +0000 Subject: ipv4: Use synchronize_rcu() during trie_rebalance() During trie_rebalance() we free memory after resizing with call_rcu(), but large updates, especially with PREEMPT_NONE configs, can cause memory stresses, so this patch calls synchronize_rcu() in tnode_free_flush() after each sync_pages to guarantee such freeing (especially before resizing the root node). The value of sync_pages = 128 is based on Pawel Staszewski's tests as the lowest which doesn't hinder updating times. (For testing purposes there was a sysfs module parameter to change it on demand, but it's removed until we're sure it could be really useful.) The patch is based on suggestions by: Paul E. McKenney Reported-by: Pawel Staszewski Tested-by: Pawel Staszewski Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net') diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 63c2fa7..58ba9f4 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -164,6 +164,14 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn); static struct tnode *halve(struct trie *t, struct tnode *tn); /* tnodes to free after resize(); protected by RTNL */ static struct tnode *tnode_free_head; +static size_t tnode_free_size; + +/* + * synchronize_rcu after call_rcu for that many pages; it should be especially + * useful before resizing the root node with PREEMPT_NONE configs; the value was + * obtained experimentally, aiming to avoid visible slowdown. + */ +static const int sync_pages = 128; static struct kmem_cache *fn_alias_kmem __read_mostly; static struct kmem_cache *trie_leaf_kmem __read_mostly; @@ -393,6 +401,8 @@ static void tnode_free_safe(struct tnode *tn) BUG_ON(IS_LEAF(tn)); tn->tnode_free = tnode_free_head; tnode_free_head = tn; + tnode_free_size += sizeof(struct tnode) + + (sizeof(struct node *) << tn->bits); } static void tnode_free_flush(void) @@ -404,6 +414,11 @@ static void tnode_free_flush(void) tn->tnode_free = NULL; tnode_free(tn); } + + if (tnode_free_size >= PAGE_SIZE * sync_pages) { + tnode_free_size = 0; + synchronize_rcu(); + } } static struct leaf *leaf_new(void) -- cgit v1.1 From be916cdebe4dc720a23b1a9bb589f2c22afd6589 Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Tue, 14 Jul 2009 09:41:00 +0000 Subject: ipv4: Fix inflate_threshold_root automatically During large updates there could be triggered warnings like: "Fix inflate_threshold_root. Now=25 size=11 bits" if inflate() of the root node isn't finished in 10 loops. It should be much rarer now, after changing the threshold from 15 to 25, and a temporary problem, so this patch tries to handle it automatically using a fix variable to increase by one inflate threshold for next root resizes (up to the 35 limit, max fix = 10). The fix variable is decreased when root's inflate() finishes below 7 loops (even if some other, smaller table/ trie is updated -- for simplicity the fix variable is global for now). Reported-by: Pawel Staszewski Reported-by: Jorge Boncompte [DTI2] Tested-by: Pawel Staszewski Signed-off-by: Jarek Poplawski Signed-off-by: Robert Olsson Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 58ba9f4..5741d13 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -327,6 +327,8 @@ static const int inflate_threshold = 50; static const int halve_threshold_root = 15; static const int inflate_threshold_root = 25; +static int inflate_threshold_root_fix; +#define INFLATE_FIX_MAX 10 /* a comment in resize() */ static void __alias_free_mem(struct rcu_head *head) { @@ -617,7 +619,8 @@ static struct node *resize(struct trie *t, struct tnode *tn) /* Keep root node larger */ if (!tn->parent) - inflate_threshold_use = inflate_threshold_root; + inflate_threshold_use = inflate_threshold_root + + inflate_threshold_root_fix; else inflate_threshold_use = inflate_threshold; @@ -641,15 +644,27 @@ static struct node *resize(struct trie *t, struct tnode *tn) } if (max_resize < 0) { - if (!tn->parent) - pr_warning("Fix inflate_threshold_root." - " Now=%d size=%d bits\n", - inflate_threshold_root, tn->bits); - else + if (!tn->parent) { + /* + * It was observed that during large updates even + * inflate_threshold_root = 35 might be needed to avoid + * this warning; but it should be temporary, so let's + * try to handle this automatically. + */ + if (inflate_threshold_root_fix < INFLATE_FIX_MAX) + inflate_threshold_root_fix++; + else + pr_warning("Fix inflate_threshold_root." + " Now=%d size=%d bits fix=%d\n", + inflate_threshold_root, tn->bits, + inflate_threshold_root_fix); + } else { pr_warning("Fix inflate_threshold." " Now=%d size=%d bits\n", inflate_threshold, tn->bits); - } + } + } else if (max_resize > 3 && !tn->parent && inflate_threshold_root_fix) + inflate_threshold_root_fix--; check_tnode(tn); -- cgit v1.1 From b902e5735272b6a79fe2853180b2ad6658aa9678 Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Tue, 14 Jul 2009 11:20:32 +0000 Subject: ipv4: fib_trie: Use tnode_get_child_rcu() and node_parent_rcu() in lookups While looking for other fib_trie problems reported by Pawel Staszewski I noticed there are a few uses of tnode_get_child() and node_parent() in lookups instead of their rcu versions. Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 5741d13..d58b491 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1465,7 +1465,7 @@ static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp, cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length), pos, bits); - n = tnode_get_child(pn, cindex); + n = tnode_get_child_rcu(pn, cindex); if (n == NULL) { #ifdef CONFIG_IP_FIB_TRIE_STATS @@ -1600,7 +1600,7 @@ backtrace: if (chopped_off <= pn->bits) { cindex &= ~(1 << (chopped_off-1)); } else { - struct tnode *parent = node_parent((struct node *) pn); + struct tnode *parent = node_parent_rcu((struct node *) pn); if (!parent) goto failed; @@ -1813,7 +1813,7 @@ static struct leaf *trie_firstleaf(struct trie *t) static struct leaf *trie_nextleaf(struct leaf *l) { struct node *c = (struct node *) l; - struct tnode *p = node_parent(c); + struct tnode *p = node_parent_rcu(c); if (!p) return NULL; /* trie with just one leaf */ -- cgit v1.1 From 3ba23ade464cca7c4a7ba5628c613339d3f2e161 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:22 +0000 Subject: RDS: Set retry_count to 2 and make modifiable via modparam This will be default cause IB connections to failover faster, but allow a longer retry count to be used if desired. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib.c | 3 +++ net/rds/ib.h | 3 +++ net/rds/ib_cm.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/ib.c b/net/rds/ib.c index b9bcd32..27abdd3 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -43,11 +43,14 @@ unsigned int fmr_pool_size = RDS_FMR_POOL_SIZE; unsigned int fmr_message_size = RDS_FMR_SIZE + 1; /* +1 allows for unaligned MRs */ +unsigned int rds_ib_retry_count = RDS_IB_DEFAULT_RETRY_COUNT; module_param(fmr_pool_size, int, 0444); MODULE_PARM_DESC(fmr_pool_size, " Max number of fmr per HCA"); module_param(fmr_message_size, int, 0444); MODULE_PARM_DESC(fmr_message_size, " Max size of a RDMA transfer"); +module_param(rds_ib_retry_count, int, 0444); +MODULE_PARM_DESC(rds_ib_retry_count, " Number of hw retries before reporting an error"); struct list_head rds_ib_devices; diff --git a/net/rds/ib.h b/net/rds/ib.h index 455ae73..420afb9 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -15,6 +15,8 @@ #define RDS_IB_DEFAULT_RECV_WR 1024 #define RDS_IB_DEFAULT_SEND_WR 256 +#define RDS_IB_DEFAULT_RETRY_COUNT 2 + #define RDS_IB_SUPPORTED_PROTOCOLS 0x00000003 /* minor versions supported */ extern struct list_head rds_ib_devices; @@ -247,6 +249,7 @@ extern struct ib_client rds_ib_client; extern unsigned int fmr_pool_size; extern unsigned int fmr_message_size; +extern unsigned int rds_ib_retry_count; extern spinlock_t ib_nodev_conns_lock; extern struct list_head ib_nodev_conns; diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index f8e40e1..605c032 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -145,7 +145,7 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn, /* XXX tune these? */ conn_param->responder_resources = 1; conn_param->initiator_depth = 1; - conn_param->retry_count = 7; + conn_param->retry_count = min_t(unsigned int, rds_ib_retry_count, 7); conn_param->rnr_retry_count = 7; if (dp) { -- cgit v1.1 From 9ddbcfa098bae757d3760dd1dbf2847a0bd5a525 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:23 +0000 Subject: RDS/IB: Improve RDS protocol version checking RDS on IB uses privdata to do protocol version negotiation. Apparently the IB stack will return a larger privdata buffer than the struct we were expecting. Just to be extra-sure, this patch adds some checks in this area. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_cm.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 605c032..0964ac5 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -98,7 +98,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even struct ib_qp_attr qp_attr; int err; - if (event->param.conn.private_data_len) { + if (event->param.conn.private_data_len >= sizeof(*dp)) { dp = event->param.conn.private_data; rds_ib_set_protocol(conn, @@ -344,19 +344,32 @@ out: return ret; } -static u32 rds_ib_protocol_compatible(const struct rds_ib_connect_private *dp) +static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event) { + const struct rds_ib_connect_private *dp = event->param.conn.private_data; u16 common; u32 version = 0; - /* rdma_cm private data is odd - when there is any private data in the + /* + * rdma_cm private data is odd - when there is any private data in the * request, we will be given a pretty large buffer without telling us the * original size. The only way to tell the difference is by looking at * the contents, which are initialized to zero. * If the protocol version fields aren't set, this is a connection attempt * from an older version. This could could be 3.0 or 2.0 - we can't tell. - * We really should have changed this for OFED 1.3 :-( */ - if (dp->dp_protocol_major == 0) + * We really should have changed this for OFED 1.3 :-( + */ + + /* Be paranoid. RDS always has privdata */ + if (!event->param.conn.private_data_len) { + printk(KERN_NOTICE "RDS incoming connection has no private data, " + "rejecting\n"); + return 0; + } + + /* Even if len is crap *now* I still want to check it. -ASG */ + if (event->param.conn.private_data_len < sizeof (*dp) + || dp->dp_protocol_major == 0) return RDS_PROTOCOL_3_0; common = be16_to_cpu(dp->dp_protocol_minor_mask) & RDS_IB_SUPPORTED_PROTOCOLS; @@ -388,7 +401,7 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, int err, destroy = 1; /* Check whether the remote protocol version matches ours. */ - version = rds_ib_protocol_compatible(dp); + version = rds_ib_protocol_compatible(event); if (!version) goto out; -- cgit v1.1 From 02a6a2592e41d27644d647f3bce23598649961bc Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:24 +0000 Subject: RDS/IB: Handle connections using RDS 3.0 wire protocol The big differences between RDS 3.0 and 3.1 are protocol-level flow control, and with 3.1 the header is in front of the data. The header always ends up in the header buffer, and the data goes in the data page. In 3.0 our "header" is a trailer, and will end up either in the data page, the header buffer, or split across the two. Since 3.1 is backwards- compatible with 3.0, we need to continue to support these cases. This patch does that -- if using RDS 3.0 wire protocol, it will copy the header from wherever it ended up into the header buffer. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib.h | 12 ++++++++++-- net/rds/ib_cm.c | 9 ++++++--- net/rds/ib_recv.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/rds/ib.h b/net/rds/ib.h index 420afb9..c0de7af 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -358,17 +358,25 @@ extern ctl_table rds_ib_sysctl_table[]; /* * Helper functions for getting/setting the header and data SGEs in * RDS packets (not RDMA) + * + * From version 3.1 onwards, header is in front of data in the sge. */ static inline struct ib_sge * rds_ib_header_sge(struct rds_ib_connection *ic, struct ib_sge *sge) { - return &sge[0]; + if (ic->conn->c_version > RDS_PROTOCOL_3_0) + return &sge[0]; + else + return &sge[1]; } static inline struct ib_sge * rds_ib_data_sge(struct rds_ib_connection *ic, struct ib_sge *sge) { - return &sge[1]; + if (ic->conn->c_version > RDS_PROTOCOL_3_0) + return &sge[1]; + else + return &sge[0]; } #endif diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 0964ac5..1eb0c29 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -101,10 +101,13 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even if (event->param.conn.private_data_len >= sizeof(*dp)) { dp = event->param.conn.private_data; - rds_ib_set_protocol(conn, + /* make sure it isn't empty data */ + if (dp->dp_protocol_major) { + rds_ib_set_protocol(conn, RDS_PROTOCOL(dp->dp_protocol_major, - dp->dp_protocol_minor)); - rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit)); + dp->dp_protocol_minor)); + rds_ib_set_flow_control(conn, be32_to_cpu(dp->dp_credit)); + } } printk(KERN_NOTICE "RDS/IB: connected to %pI4 version %u.%u%s\n", diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index 5709bad..28bdcdc 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -555,6 +555,47 @@ u64 rds_ib_piggyb_ack(struct rds_ib_connection *ic) return rds_ib_get_ack(ic); } +static struct rds_header *rds_ib_get_header(struct rds_connection *conn, + struct rds_ib_recv_work *recv, + u32 data_len) +{ + struct rds_ib_connection *ic = conn->c_transport_data; + void *hdr_buff = &ic->i_recv_hdrs[recv - ic->i_recvs]; + void *addr; + u32 misplaced_hdr_bytes; + + /* + * Support header at the front (RDS 3.1+) as well as header-at-end. + * + * Cases: + * 1) header all in header buff (great!) + * 2) header all in data page (copy all to header buff) + * 3) header split across hdr buf + data page + * (move bit in hdr buff to end before copying other bit from data page) + */ + if (conn->c_version > RDS_PROTOCOL_3_0 || data_len == RDS_FRAG_SIZE) + return hdr_buff; + + if (data_len <= (RDS_FRAG_SIZE - sizeof(struct rds_header))) { + addr = kmap_atomic(recv->r_frag->f_page, KM_SOFTIRQ0); + memcpy(hdr_buff, + addr + recv->r_frag->f_offset + data_len, + sizeof(struct rds_header)); + kunmap_atomic(addr, KM_SOFTIRQ0); + return hdr_buff; + } + + misplaced_hdr_bytes = (sizeof(struct rds_header) - (RDS_FRAG_SIZE - data_len)); + + memmove(hdr_buff + misplaced_hdr_bytes, hdr_buff, misplaced_hdr_bytes); + + addr = kmap_atomic(recv->r_frag->f_page, KM_SOFTIRQ0); + memcpy(hdr_buff, addr + recv->r_frag->f_offset + data_len, + sizeof(struct rds_header) - misplaced_hdr_bytes); + kunmap_atomic(addr, KM_SOFTIRQ0); + return hdr_buff; +} + /* * It's kind of lame that we're copying from the posted receive pages into * long-lived bitmaps. We could have posted the bitmaps and rdma written into @@ -667,7 +708,7 @@ static void rds_ib_process_recv(struct rds_connection *conn, } byte_len -= sizeof(struct rds_header); - ihdr = &ic->i_recv_hdrs[recv - ic->i_recvs]; + ihdr = rds_ib_get_header(conn, recv, byte_len); /* Validate the checksum. */ if (!rds_message_verify_checksum(ihdr)) { -- cgit v1.1 From 8dacd57e7ebc307d4d7c27c5d1caada4c4e63ebd Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:25 +0000 Subject: RDS/IB: Fix printk to indicate remote IP, not local Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 1eb0c29..f621086 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -111,7 +111,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even } printk(KERN_NOTICE "RDS/IB: connected to %pI4 version %u.%u%s\n", - &conn->c_laddr, + &conn->c_faddr, RDS_PROTOCOL_MAJOR(conn->c_version), RDS_PROTOCOL_MINOR(conn->c_version), ic->i_flowctl ? ", flow control" : ""); -- cgit v1.1 From 92c330b9e93ce70a8c45a6b8b0a551321d783feb Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:26 +0000 Subject: RDS/RDMA: Fix cut-n-paste errors in printks in rdma_transport.c Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/rdma_transport.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index 7d0f901..981a5e6 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -101,7 +101,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, break; case RDMA_CM_EVENT_DISCONNECTED: - printk(KERN_WARNING "RDS/IW: DISCONNECT event - dropping connection " + printk(KERN_WARNING "RDS/RDMA: DISCONNECT event - dropping connection " "%pI4->%pI4\n", &conn->c_laddr, &conn->c_faddr); rds_conn_drop(conn); @@ -132,7 +132,7 @@ static int __init rds_rdma_listen_init(void) cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP); if (IS_ERR(cm_id)) { ret = PTR_ERR(cm_id); - printk(KERN_ERR "RDS/IW: failed to setup listener, " + printk(KERN_ERR "RDS/RDMA: failed to setup listener, " "rdma_create_id() returned %d\n", ret); goto out; } @@ -147,14 +147,14 @@ static int __init rds_rdma_listen_init(void) */ ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); if (ret) { - printk(KERN_ERR "RDS/IW: failed to setup listener, " + printk(KERN_ERR "RDS/RDMA: failed to setup listener, " "rdma_bind_addr() returned %d\n", ret); goto out; } ret = rdma_listen(cm_id, 128); if (ret) { - printk(KERN_ERR "RDS/IW: failed to setup listener, " + printk(KERN_ERR "RDS/RDMA: failed to setup listener, " "rdma_listen() returned %d\n", ret); goto out; } -- cgit v1.1 From 597ddd50e1c07ac55ac7742442690efcf16a37f5 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:27 +0000 Subject: RDS/IB: Rename byte_len to data_len to enhance readability Of course len is in bytes. Calling it data_len hopefully indicates a little better what the variable is actually for. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_recv.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index 28bdcdc..cd7a6cf 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -686,7 +686,7 @@ struct rds_ib_ack_state { }; static void rds_ib_process_recv(struct rds_connection *conn, - struct rds_ib_recv_work *recv, u32 byte_len, + struct rds_ib_recv_work *recv, u32 data_len, struct rds_ib_ack_state *state) { struct rds_ib_connection *ic = conn->c_transport_data; @@ -696,9 +696,9 @@ static void rds_ib_process_recv(struct rds_connection *conn, /* XXX shut down the connection if port 0,0 are seen? */ rdsdebug("ic %p ibinc %p recv %p byte len %u\n", ic, ibinc, recv, - byte_len); + data_len); - if (byte_len < sizeof(struct rds_header)) { + if (data_len < sizeof(struct rds_header)) { rds_ib_conn_error(conn, "incoming message " "from %pI4 didn't inclue a " "header, disconnecting and " @@ -706,9 +706,9 @@ static void rds_ib_process_recv(struct rds_connection *conn, &conn->c_faddr); return; } - byte_len -= sizeof(struct rds_header); + data_len -= sizeof(struct rds_header); - ihdr = rds_ib_get_header(conn, recv, byte_len); + ihdr = rds_ib_get_header(conn, recv, data_len); /* Validate the checksum. */ if (!rds_message_verify_checksum(ihdr)) { @@ -728,7 +728,7 @@ static void rds_ib_process_recv(struct rds_connection *conn, if (ihdr->h_credit) rds_ib_send_add_credits(conn, ihdr->h_credit); - if (ihdr->h_sport == 0 && ihdr->h_dport == 0 && byte_len == 0) { + if (ihdr->h_sport == 0 && ihdr->h_dport == 0 && data_len == 0) { /* This is an ACK-only packet. The fact that it gets * special treatment here is that historically, ACKs * were rather special beasts. -- cgit v1.1 From 9099707ded4b3aeda7b1a6c1c87076bd18578d24 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:28 +0000 Subject: RDS: Don't set c_version in __rds_conn_create() Protocol negotiation is logically a property of the transports, so rds core need not set it. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/connection.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/rds/connection.c b/net/rds/connection.c index d14445c..605fe3f 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -155,7 +155,6 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, } INIT_HLIST_NODE(&conn->c_hash_node); - conn->c_version = RDS_PROTOCOL_3_0; conn->c_laddr = laddr; conn->c_faddr = faddr; spin_lock_init(&conn->c_lock); -- cgit v1.1 From e11d912a7dd4dfe388f38ba3080a6d067a57b23d Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:29 +0000 Subject: RDS/IB: Move tx/rx ring init and refill to later Since RDS 3.0 and 3.1 have different packet formats, we need to wait until after protocol negotiation is complete to layout the rx buffers. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_cm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index f621086..0ad749c 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -116,6 +116,16 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even RDS_PROTOCOL_MINOR(conn->c_version), ic->i_flowctl ? ", flow control" : ""); + /* + * Init rings and fill recv. this needs to wait until protocol negotiation + * is complete, since ring layout is different from 3.0 to 3.1. + */ + rds_ib_send_init_ring(ic); + rds_ib_recv_init_ring(ic); + /* Post receive buffers - as a side effect, this will update + * the posted credit count. */ + rds_ib_recv_refill(conn, GFP_KERNEL, GFP_HIGHUSER, 1); + /* Tune RNR behavior */ rds_ib_tune_rnr(ic, &qp_attr); @@ -324,7 +334,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) rdsdebug("send allocation failed\n"); goto out; } - rds_ib_send_init_ring(ic); + memset(ic->i_sends, 0, ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work)); ic->i_recvs = vmalloc(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work)); if (ic->i_recvs == NULL) { @@ -332,14 +342,10 @@ static int rds_ib_setup_qp(struct rds_connection *conn) rdsdebug("recv allocation failed\n"); goto out; } + memset(ic->i_recvs, 0, ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work)); - rds_ib_recv_init_ring(ic); rds_ib_recv_init_ack(ic); - /* Post receive buffers - as a side effect, this will update - * the posted credit count. */ - rds_ib_recv_refill(conn, GFP_KERNEL, GFP_HIGHUSER, 1); - rdsdebug("conn %p pd %p mr %p cq %p %p\n", conn, ic->i_pd, ic->i_mr, ic->i_send_cq, ic->i_recv_cq); -- cgit v1.1 From 68cb01c1ba312add7c7cc7da1bbe98b3071904d1 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:30 +0000 Subject: RDS/IB: Disable flow control in sysctl and explain why Backwards compatibility with rds 3.0 causes protocol- based flow control to be disabled as a side-effect. I don't want to pull out FC support from the IB transport but I do want to document and keep the sysctl consistent if possible. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_sysctl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/ib_sysctl.c b/net/rds/ib_sysctl.c index d87830d..84b5ffcb 100644 --- a/net/rds/ib_sysctl.c +++ b/net/rds/ib_sysctl.c @@ -53,7 +53,17 @@ unsigned long rds_ib_sysctl_max_unsig_bytes = (16 << 20); static unsigned long rds_ib_sysctl_max_unsig_bytes_min = 1; static unsigned long rds_ib_sysctl_max_unsig_bytes_max = ~0UL; -unsigned int rds_ib_sysctl_flow_control = 1; +/* + * This sysctl does nothing. + * + * Backwards compatibility with RDS 3.0 wire protocol + * disables initial FC credit exchange. + * If it's ever possible to drop 3.0 support, + * setting this to 1 and moving init/refill of send/recv + * rings from ib_cm_connect_complete() back into ib_setup_qp() + * will cause credits to be added before protocol negotiation. + */ +unsigned int rds_ib_sysctl_flow_control = 0; ctl_table rds_ib_sysctl_table[] = { { -- cgit v1.1 From fdf6e6b4afd8a56fa58f70a3950bd7ea7fbaef5f Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:31 +0000 Subject: RDS/IB: Drop connection when a fatal QP event is received Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib_cm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 0ad749c..c2d372f 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -203,9 +203,9 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data) rdma_notify(ic->i_cm_id, IB_EVENT_COMM_EST); break; default: - printk(KERN_WARNING "RDS/ib: unhandled QP event %u " - "on connection to %pI4\n", event->event, - &conn->c_faddr); + rds_ib_conn_error(conn, "RDS/IB: Fatal QP Event %u " + "- connection %pI4->%pI4, reconnecting\n", + event->event, &conn->c_laddr, &conn->c_faddr); break; } } -- cgit v1.1 From edacaeae52ade6cbb3a0704db32a9fb4a219dee3 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:32 +0000 Subject: RDS: Fix completion notifications on blocking sockets Completion or congestion notifications were not being checked if the socket went to sleep. This patch fixes that. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/recv.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/rds/recv.c b/net/rds/recv.c index f2118c5..86bc1a0 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -409,18 +409,18 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, if (msg_flags & MSG_OOB) goto out; - /* If there are pending notifications, do those - and nothing else */ - if (!list_empty(&rs->rs_notify_queue)) { - ret = rds_notify_queue_get(rs, msg); - goto out; - } + while (1) { + /* If there are pending notifications, do those - and nothing else */ + if (!list_empty(&rs->rs_notify_queue)) { + ret = rds_notify_queue_get(rs, msg); + break; + } - if (rs->rs_cong_notify) { - ret = rds_notify_cong(rs, msg); - goto out; - } + if (rs->rs_cong_notify) { + ret = rds_notify_cong(rs, msg); + break; + } - while (1) { if (!rds_next_incoming(rs, &inc)) { if (nonblock) { ret = -EAGAIN; @@ -428,7 +428,9 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, } timeo = wait_event_interruptible_timeout(*sk->sk_sleep, - rds_next_incoming(rs, &inc), + (!list_empty(&rs->rs_notify_queue) + || rs->rs_cong_notify + || rds_next_incoming(rs, &inc)), timeo); rdsdebug("recvmsg woke inc %p timeo %ld\n", inc, timeo); -- cgit v1.1 From a870d62726721785c34fa73d852bd35e5d1b295b Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:33 +0000 Subject: RDS/IB: Always use PAGE_SIZE for FMR page size While FMRs allow significant flexibility in what size of pages they can use, we really just want FMR pages to match CPU page size. Roland says we can count on this always being supported, so this simplifies things. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib.c | 3 --- net/rds/ib.h | 3 --- net/rds/ib_rdma.c | 12 ++++++------ 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/rds/ib.c b/net/rds/ib.c index 27abdd3..868559a 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -85,9 +85,6 @@ void rds_ib_add_one(struct ib_device *device) rds_ibdev->max_wrs = dev_attr->max_qp_wr; rds_ibdev->max_sge = min(dev_attr->max_sge, RDS_IB_MAX_SGE); - rds_ibdev->fmr_page_shift = max(9, ffs(dev_attr->page_size_cap) - 1); - rds_ibdev->fmr_page_size = 1 << rds_ibdev->fmr_page_shift; - rds_ibdev->fmr_page_mask = ~((u64) rds_ibdev->fmr_page_size - 1); rds_ibdev->fmr_max_remaps = dev_attr->max_map_per_fmr?: 32; rds_ibdev->max_fmrs = dev_attr->max_fmr ? min_t(unsigned int, dev_attr->max_fmr, fmr_pool_size) : diff --git a/net/rds/ib.h b/net/rds/ib.h index c0de7af..1378b85 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -159,9 +159,6 @@ struct rds_ib_device { struct ib_pd *pd; struct ib_mr *mr; struct rds_ib_mr_pool *mr_pool; - int fmr_page_shift; - int fmr_page_size; - u64 fmr_page_mask; unsigned int fmr_max_remaps; unsigned int max_fmrs; int max_sge; diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index 81033af..ef3ab5b 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -211,7 +211,7 @@ struct rds_ib_mr_pool *rds_ib_create_mr_pool(struct rds_ib_device *rds_ibdev) pool->fmr_attr.max_pages = fmr_message_size; pool->fmr_attr.max_maps = rds_ibdev->fmr_max_remaps; - pool->fmr_attr.page_shift = rds_ibdev->fmr_page_shift; + pool->fmr_attr.page_shift = PAGE_SHIFT; pool->max_free_pinned = rds_ibdev->max_fmrs * fmr_message_size / 4; /* We never allow more than max_items MRs to be allocated. @@ -349,13 +349,13 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm unsigned int dma_len = ib_sg_dma_len(dev, &scat[i]); u64 dma_addr = ib_sg_dma_address(dev, &scat[i]); - if (dma_addr & ~rds_ibdev->fmr_page_mask) { + if (dma_addr & ~PAGE_MASK) { if (i > 0) return -EINVAL; else ++page_cnt; } - if ((dma_addr + dma_len) & ~rds_ibdev->fmr_page_mask) { + if ((dma_addr + dma_len) & ~PAGE_MASK) { if (i < sg_dma_len - 1) return -EINVAL; else @@ -365,7 +365,7 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm len += dma_len; } - page_cnt += len >> rds_ibdev->fmr_page_shift; + page_cnt += len >> PAGE_SHIFT; if (page_cnt > fmr_message_size) return -EINVAL; @@ -378,9 +378,9 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibm unsigned int dma_len = ib_sg_dma_len(dev, &scat[i]); u64 dma_addr = ib_sg_dma_address(dev, &scat[i]); - for (j = 0; j < dma_len; j += rds_ibdev->fmr_page_size) + for (j = 0; j < dma_len; j += PAGE_SIZE) dma_pages[page_cnt++] = - (dma_addr & rds_ibdev->fmr_page_mask) + j; + (dma_addr & PAGE_MASK) + j; } ret = ib_map_phys_fmr(ibmr->fmr, -- cgit v1.1 From 404bb72a56e553febe1055f98347a7a3e3145759 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:34 +0000 Subject: RDS/IW: Remove page_shift variable from iwarp transport The existing code treated page_shift as a variable, when in fact we always want to have the fastreg page size be the same as the arch's page size -- and it is, so this doesn't need to be a variable. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/iw.c | 2 -- net/rds/iw.h | 1 - net/rds/iw_rdma.c | 28 ++++++++++------------------ net/rds/iw_send.c | 2 +- 4 files changed, 11 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/rds/iw.c b/net/rds/iw.c index d16e1cb..2e6b495 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -83,8 +83,6 @@ void rds_iw_add_one(struct ib_device *device) rds_iwdev->max_wrs = dev_attr->max_qp_wr; rds_iwdev->max_sge = min(dev_attr->max_sge, RDS_IW_MAX_SGE); - rds_iwdev->page_shift = max(PAGE_SHIFT, ffs(dev_attr->page_size_cap) - 1); - rds_iwdev->dev = device; rds_iwdev->pd = ib_alloc_pd(device); if (IS_ERR(rds_iwdev->pd)) diff --git a/net/rds/iw.h b/net/rds/iw.h index 0715dde..dd72b62 100644 --- a/net/rds/iw.h +++ b/net/rds/iw.h @@ -181,7 +181,6 @@ struct rds_iw_device { struct ib_pd *pd; struct ib_mr *mr; struct rds_iw_mr_pool *mr_pool; - int page_shift; int max_sge; unsigned int max_wrs; unsigned int dma_local_lkey:1; diff --git a/net/rds/iw_rdma.c b/net/rds/iw_rdma.c index dcdb37d..de4a1b1 100644 --- a/net/rds/iw_rdma.c +++ b/net/rds/iw_rdma.c @@ -263,18 +263,12 @@ static void rds_iw_set_scatterlist(struct rds_iw_scatterlist *sg, } static u64 *rds_iw_map_scatterlist(struct rds_iw_device *rds_iwdev, - struct rds_iw_scatterlist *sg, - unsigned int dma_page_shift) + struct rds_iw_scatterlist *sg) { struct ib_device *dev = rds_iwdev->dev; u64 *dma_pages = NULL; - u64 dma_mask; - unsigned int dma_page_size; int i, j, ret; - dma_page_size = 1 << dma_page_shift; - dma_mask = dma_page_size - 1; - WARN_ON(sg->dma_len); sg->dma_len = ib_dma_map_sg(dev, sg->list, sg->len, DMA_BIDIRECTIONAL); @@ -295,18 +289,18 @@ static u64 *rds_iw_map_scatterlist(struct rds_iw_device *rds_iwdev, sg->bytes += dma_len; end_addr = dma_addr + dma_len; - if (dma_addr & dma_mask) { + if (dma_addr & PAGE_MASK) { if (i > 0) goto out_unmap; - dma_addr &= ~dma_mask; + dma_addr &= ~PAGE_MASK; } - if (end_addr & dma_mask) { + if (end_addr & PAGE_MASK) { if (i < sg->dma_len - 1) goto out_unmap; - end_addr = (end_addr + dma_mask) & ~dma_mask; + end_addr = (end_addr + PAGE_MASK) & ~PAGE_MASK; } - sg->dma_npages += (end_addr - dma_addr) >> dma_page_shift; + sg->dma_npages += (end_addr - dma_addr) >> PAGE_SHIFT; } /* Now gather the dma addrs into one list */ @@ -325,8 +319,8 @@ static u64 *rds_iw_map_scatterlist(struct rds_iw_device *rds_iwdev, u64 end_addr; end_addr = dma_addr + dma_len; - dma_addr &= ~dma_mask; - for (; dma_addr < end_addr; dma_addr += dma_page_size) + dma_addr &= ~PAGE_MASK; + for (; dma_addr < end_addr; dma_addr += PAGE_SIZE) dma_pages[j++] = dma_addr; BUG_ON(j > sg->dma_npages); } @@ -727,7 +721,7 @@ static int rds_iw_rdma_build_fastreg(struct rds_iw_mapping *mapping) f_wr.wr.fast_reg.rkey = mapping->m_rkey; f_wr.wr.fast_reg.page_list = ibmr->page_list; f_wr.wr.fast_reg.page_list_len = mapping->m_sg.dma_len; - f_wr.wr.fast_reg.page_shift = ibmr->device->page_shift; + f_wr.wr.fast_reg.page_shift = PAGE_SHIFT; f_wr.wr.fast_reg.access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_WRITE; @@ -780,9 +774,7 @@ static int rds_iw_map_fastreg(struct rds_iw_mr_pool *pool, rds_iw_set_scatterlist(&mapping->m_sg, sg, sg_len); - dma_pages = rds_iw_map_scatterlist(rds_iwdev, - &mapping->m_sg, - rds_iwdev->page_shift); + dma_pages = rds_iw_map_scatterlist(rds_iwdev, &mapping->m_sg); if (IS_ERR(dma_pages)) { ret = PTR_ERR(dma_pages); dma_pages = NULL; diff --git a/net/rds/iw_send.c b/net/rds/iw_send.c index 44a6a05..1f5abe3 100644 --- a/net/rds/iw_send.c +++ b/net/rds/iw_send.c @@ -779,7 +779,7 @@ static void rds_iw_build_send_fastreg(struct rds_iw_device *rds_iwdev, struct rd send->s_wr.wr.fast_reg.rkey = send->s_mr->rkey; send->s_wr.wr.fast_reg.page_list = send->s_page_list; send->s_wr.wr.fast_reg.page_list_len = nent; - send->s_wr.wr.fast_reg.page_shift = rds_iwdev->page_shift; + send->s_wr.wr.fast_reg.page_shift = PAGE_SHIFT; send->s_wr.wr.fast_reg.access_flags = IB_ACCESS_REMOTE_WRITE; send->s_wr.wr.fast_reg.iova_start = sg_addr; -- cgit v1.1 From ed9e352a350ec85eb354046e0db6a86019620f53 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:35 +0000 Subject: RDS/IW: Remove dead code In iWARP code, node_type will always be RNIC Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/iw.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/rds/iw.c b/net/rds/iw.c index 2e6b495..f5e9a29 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -89,15 +89,10 @@ void rds_iw_add_one(struct ib_device *device) goto free_dev; if (!rds_iwdev->dma_local_lkey) { - if (device->node_type != RDMA_NODE_RNIC) { - rds_iwdev->mr = ib_get_dma_mr(rds_iwdev->pd, - IB_ACCESS_LOCAL_WRITE); - } else { - rds_iwdev->mr = ib_get_dma_mr(rds_iwdev->pd, - IB_ACCESS_REMOTE_READ | - IB_ACCESS_REMOTE_WRITE | - IB_ACCESS_LOCAL_WRITE); - } + rds_iwdev->mr = ib_get_dma_mr(rds_iwdev->pd, + IB_ACCESS_REMOTE_READ | + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_LOCAL_WRITE); if (IS_ERR(rds_iwdev->mr)) goto err_pd; } else -- cgit v1.1 From cb24405e67e56cbef51b5e4d0bb0a0fde167261f Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 17 Jul 2009 13:13:36 +0000 Subject: RDS: Refactor end of __conn_create for readability Add a comment for what's going on. Remove negative logic. I find this much easier to understand quickly, although there are a few lines duplicated. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/connection.c | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/rds/connection.c b/net/rds/connection.c index 605fe3f..b420a20 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -126,7 +126,7 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, struct rds_transport *trans, gfp_t gfp, int is_outgoing) { - struct rds_connection *conn, *tmp, *parent = NULL; + struct rds_connection *conn, *parent = NULL; struct hlist_head *head = rds_conn_bucket(laddr, faddr); unsigned long flags; int ret; @@ -210,26 +210,40 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr, trans->t_name ? trans->t_name : "[unknown]", is_outgoing ? "(outgoing)" : ""); + /* + * Since we ran without holding the conn lock, someone could + * have created the same conn (either normal or passive) in the + * interim. We check while holding the lock. If we won, we complete + * init and return our conn. If we lost, we rollback and return the + * other one. + */ spin_lock_irqsave(&rds_conn_lock, flags); - if (parent == NULL) { - tmp = rds_conn_lookup(head, laddr, faddr, trans); - if (tmp == NULL) - hlist_add_head(&conn->c_hash_node, head); - } else { - tmp = parent->c_passive; - if (!tmp) + if (parent) { + /* Creating passive conn */ + if (parent->c_passive) { + trans->conn_free(conn->c_transport_data); + kmem_cache_free(rds_conn_slab, conn); + conn = parent->c_passive; + } else { parent->c_passive = conn; - } - - if (tmp) { - trans->conn_free(conn->c_transport_data); - kmem_cache_free(rds_conn_slab, conn); - conn = tmp; + rds_cong_add_conn(conn); + rds_conn_count++; + } } else { - rds_cong_add_conn(conn); - rds_conn_count++; + /* Creating normal conn */ + struct rds_connection *found; + + found = rds_conn_lookup(head, laddr, faddr, trans); + if (found) { + trans->conn_free(conn->c_transport_data); + kmem_cache_free(rds_conn_slab, conn); + conn = found; + } else { + hlist_add_head(&conn->c_hash_node, head); + rds_cong_add_conn(conn); + rds_conn_count++; + } } - spin_unlock_irqrestore(&rds_conn_lock, flags); out: -- cgit v1.1 From 3c2b8d180a47d041e9101ead27e49d237df0f1d1 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 21 Jul 2009 11:13:25 -0700 Subject: mcastv6: Local variable shadows function argument The local variable 'idev' shadows the function argument 'idev' to ip6_mc_add_src(). Fixed by removing the local declaration, as pmc->idev should be identical with 'idev' passed as argument. Signed-off-by: Gerrit Renker Acked-by: David L Stevens Signed-off-by: David S. Miller --- net/ipv6/mcast.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 4b264ed..71c3dac 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2107,7 +2107,6 @@ static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca, for (j=0; jmca_sfcount[MCAST_EXCLUDE] != 0)) { - struct inet6_dev *idev = pmc->idev; struct ip6_sf_list *psf; /* filter mode change */ -- cgit v1.1 From f0166e5e3cdab66d5a31f796ce18e21fd3ce99dc Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 23 Jul 2009 16:56:29 +0400 Subject: ieee802154: move headers out of extra directory include/net/ieee802154/af_ieee802154.h (and others) naming seems to be too long and redundant. Drop one level of subdirectories. Signed-off-by: Dmitry Eremin-Solenikov --- net/ieee802154/af_ieee802154.c | 4 ++-- net/ieee802154/dgram.c | 6 +++--- net/ieee802154/netlink.c | 6 +++--- net/ieee802154/raw.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 3bb6bdb..69c8d92 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -34,8 +34,8 @@ #include #include -#include -#include +#include +#include #include "af802154.h" diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 14d3984..53dd912 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -26,9 +26,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 27eda9f..a615b9d 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -27,9 +27,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include static unsigned int ieee802154_seq_num; diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index fca44d5..ea8d1f1 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include "af802154.h" -- cgit v1.1 From c1dc13e9d0bc35a8d85bf4238c48c1b627d48f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 21 Jul 2009 01:57:57 +0000 Subject: Phonet: sockets list through proc_fs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides a list of sockets with their Phonet bind addresses and some socket debug informations through /proc/net/phonet. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/pn_dev.c | 7 ++++ net/phonet/socket.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) (limited to 'net') diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index b0d6ddd..5107b79 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -218,6 +218,11 @@ static int phonet_init_net(struct net *net) if (!pnn) return -ENOMEM; + if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) { + kfree(pnn); + return -ENOMEM; + } + INIT_LIST_HEAD(&pnn->pndevs.list); spin_lock_init(&pnn->pndevs.lock); net_assign_generic(net, phonet_net_id, pnn); @@ -233,6 +238,8 @@ static void phonet_exit_net(struct net *net) for_each_netdev(net, dev) phonet_device_destroy(dev); rtnl_unlock(); + + proc_net_remove(net, "phonet"); kfree(pnn); } diff --git a/net/phonet/socket.c b/net/phonet/socket.c index ada2a35..aa1617a 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -412,3 +412,99 @@ found: return 0; } EXPORT_SYMBOL(pn_sock_get_port); + +static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos) +{ + struct net *net = seq_file_net(seq); + struct hlist_node *node; + struct sock *sknode; + + sk_for_each(sknode, node, &pnsocks.hlist) { + if (!net_eq(net, sock_net(sknode))) + continue; + if (!pos) + return sknode; + pos--; + } + return NULL; +} + +static struct sock *pn_sock_get_next(struct seq_file *seq, struct sock *sk) +{ + struct net *net = seq_file_net(seq); + + do + sk = sk_next(sk); + while (sk && !net_eq(net, sock_net(sk))); + + return sk; +} + +static void *pn_sock_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(pnsocks.lock) +{ + spin_lock_bh(&pnsocks.lock); + return *pos ? pn_sock_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; +} + +static void *pn_sock_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct sock *sk; + + if (v == SEQ_START_TOKEN) + sk = pn_sock_get_idx(seq, 0); + else + sk = pn_sock_get_next(seq, v); + (*pos)++; + return sk; +} + +static void pn_sock_seq_stop(struct seq_file *seq, void *v) + __releases(pnsocks.lock) +{ + spin_unlock_bh(&pnsocks.lock); +} + +static int pn_sock_seq_show(struct seq_file *seq, void *v) +{ + int len; + + if (v == SEQ_START_TOKEN) + seq_printf(seq, "%s%n", "pt loc rem rs st tx_queue rx_queue " + " uid inode ref pointer drops", &len); + else { + struct sock *sk = v; + struct pn_sock *pn = pn_sk(sk); + + seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu " + "%d %p %d%n", + sk->sk_protocol, pn->sobject, 0, pn->resource, + sk->sk_state, + sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), + sock_i_uid(sk), sock_i_ino(sk), + atomic_read(&sk->sk_refcnt), sk, + atomic_read(&sk->sk_drops), &len); + } + seq_printf(seq, "%*s\n", 127 - len, ""); + return 0; +} + +static const struct seq_operations pn_sock_seq_ops = { + .start = pn_sock_seq_start, + .next = pn_sock_seq_next, + .stop = pn_sock_seq_stop, + .show = pn_sock_seq_show, +}; + +static int pn_sock_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pn_sock_seq_ops); +} + +const struct file_operations pn_sock_seq_fops = { + .owner = THIS_MODULE, + .open = pn_sock_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; -- cgit v1.1 From 2e2fb4b33d62c742019774c5e6e47711a3d60505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 21 Jul 2009 01:57:59 +0000 Subject: Phonet: account for dropped RX packets 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 --- net/phonet/pep.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/pep.c b/net/phonet/pep.c index eef833e..b8252d2 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -346,8 +346,10 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) break; case PNS_PEP_CTRL_REQ: - if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) + if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { + atomic_inc(&sk->sk_drops); break; + } __skb_pull(skb, 4); queue = &pn->ctrlreq_queue; goto queue; @@ -358,10 +360,13 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) err = sock_queue_rcv_skb(sk, skb); if (!err) return 0; + if (err == -ENOMEM) + atomic_inc(&sk->sk_drops); break; } if (pn->rx_credits == 0) { + atomic_inc(&sk->sk_drops); err = -ENOBUFS; break; } -- cgit v1.1 From 2eee40c7f7c3734b28456169b2945e07d5ac0e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 21 Jul 2009 01:57:58 +0000 Subject: Phonet: dropped datagrams accounting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-socket drop count is visible via /proc/net/phonet. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/datagram.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index e087862..ef5c75c 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -159,8 +159,11 @@ out_nofree: static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb) { int err = sock_queue_rcv_skb(sk, skb); - if (err < 0) + if (err < 0) { kfree_skb(skb); + if (err == -ENOMEM) + atomic_inc(&sk->sk_drops); + } return err ? NET_RX_DROP : NET_RX_SUCCESS; } -- cgit v1.1 From 40d4e3dfc2f56ad85ecca29c7f9a0194bbb833c0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 Jul 2009 21:57:59 +0000 Subject: af_packet: style cleanups Some style cleanups to match current code practices. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/packet/af_packet.c | 246 ++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 124 deletions(-) (limited to 'net') diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index ebe5718..d3d52c6 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -137,8 +137,7 @@ dev->hard_header == NULL (ll header is added by device, we cannot control it) /* Private packet socket structures. */ -struct packet_mclist -{ +struct packet_mclist { struct packet_mclist *next; int ifindex; int count; @@ -149,8 +148,7 @@ struct packet_mclist /* identical to struct packet_mreq except it has * a longer address field. */ -struct packet_mreq_max -{ +struct packet_mreq_max { int mr_ifindex; unsigned short mr_type; unsigned short mr_alen; @@ -162,7 +160,7 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing, int tx_ring); struct packet_ring_buffer { - char * *pg_vec; + char **pg_vec; unsigned int head; unsigned int frames_per_block; unsigned int frame_size; @@ -239,7 +237,7 @@ static void __packet_set_status(struct packet_sock *po, void *frame, int status) flush_dcache_page(virt_to_page(&h.h2->tp_status)); break; default: - printk(KERN_ERR "TPACKET version not supported\n"); + pr_err("TPACKET version not supported\n"); BUG(); } @@ -265,7 +263,7 @@ static int __packet_get_status(struct packet_sock *po, void *frame) flush_dcache_page(virt_to_page(&h.h2->tp_status)); return h.h2->tp_status; default: - printk(KERN_ERR "TPACKET version not supported\n"); + pr_err("TPACKET version not supported\n"); BUG(); return 0; } @@ -327,7 +325,7 @@ static void packet_sock_destruct(struct sock *sk) WARN_ON(atomic_read(&sk->sk_wmem_alloc)); if (!sock_flag(sk, SOCK_DEAD)) { - printk("Attempt to release alive packet socket: %p\n", sk); + pr_err("Attempt to release alive packet socket: %p\n", sk); return; } @@ -339,7 +337,8 @@ static const struct proto_ops packet_ops; static const struct proto_ops packet_ops_spkt; -static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { struct sock *sk; struct sockaddr_pkt *spkt; @@ -368,7 +367,8 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct if (dev_net(dev) != sock_net(sk)) goto out; - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) + skb = skb_share_check(skb, GFP_ATOMIC); + if (skb == NULL) goto oom; /* drop any routing info */ @@ -394,7 +394,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct * to prevent sockets using all the memory up. */ - if (sock_queue_rcv_skb(sk,skb) == 0) + if (sock_queue_rcv_skb(sk, skb) == 0) return 0; out: @@ -413,25 +413,23 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; - struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name; + struct sockaddr_pkt *saddr = (struct sockaddr_pkt *)msg->msg_name; struct sk_buff *skb; struct net_device *dev; - __be16 proto=0; + __be16 proto = 0; int err; /* * Get and verify the address. */ - if (saddr) - { + if (saddr) { if (msg->msg_namelen < sizeof(struct sockaddr)) - return(-EINVAL); - if (msg->msg_namelen==sizeof(struct sockaddr_pkt)) - proto=saddr->spkt_protocol; - } - else - return(-ENOTCONN); /* SOCK_PACKET must be sent giving an address */ + return -EINVAL; + if (msg->msg_namelen == sizeof(struct sockaddr_pkt)) + proto = saddr->spkt_protocol; + } else + return -ENOTCONN; /* SOCK_PACKET must be sent giving an address */ /* * Find the device first to size check it @@ -448,8 +446,8 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, goto out_unlock; /* - * You may not queue a frame bigger than the mtu. This is the lowest level - * raw protocol and you must do your own fragmentation at this level. + * You may not queue a frame bigger than the mtu. This is the lowest level + * raw protocol and you must do your own fragmentation at this level. */ err = -EMSGSIZE; @@ -460,9 +458,9 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, skb = sock_wmalloc(sk, len + LL_RESERVED_SPACE(dev), 0, GFP_KERNEL); /* - * If the write buffer is full, then tough. At this level the user gets to - * deal with the problem - do your own algorithmic backoffs. That's far - * more flexible. + * If the write buffer is full, then tough. At this level the user + * gets to deal with the problem - do your own algorithmic backoffs. + * That's far more flexible. */ if (skb == NULL) @@ -488,7 +486,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, } /* Returns -EFAULT on error */ - err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); skb->protocol = proto; skb->dev = dev; skb->priority = sk->sk_priority; @@ -501,7 +499,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, dev_queue_xmit(skb); dev_put(dev); - return(len); + return len; out_free: kfree_skb(skb); @@ -537,12 +535,13 @@ static inline unsigned int run_filter(struct sk_buff *skb, struct sock *sk, we will not harm anyone. */ -static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +static int packet_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { struct sock *sk; struct sockaddr_ll *sll; struct packet_sock *po; - u8 * skb_head = skb->data; + u8 *skb_head = skb->data; int skb_len = skb->len; unsigned int snaplen, res; @@ -648,7 +647,8 @@ drop: } #ifdef CONFIG_PACKET_MMAP -static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { struct sock *sk; struct packet_sock *po; @@ -658,7 +658,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe struct tpacket2_hdr *h2; void *raw; } h; - u8 * skb_head = skb->data; + u8 *skb_head = skb->data; int skb_len = skb->len; unsigned int snaplen, res; unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER; @@ -821,7 +821,7 @@ ring_is_full: static void tpacket_destruct_skb(struct sk_buff *skb) { struct packet_sock *po = pkt_sk(skb->sk); - void * ph; + void *ph; BUG_ON(skb == NULL); @@ -836,9 +836,9 @@ static void tpacket_destruct_skb(struct sk_buff *skb) sock_wfree(skb); } -static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff * skb, - void * frame, struct net_device *dev, int size_max, - __be16 proto, unsigned char * addr) +static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, + void *frame, struct net_device *dev, int size_max, + __be16 proto, unsigned char *addr) { union { struct tpacket_hdr *h1; @@ -867,8 +867,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff * skb, break; } if (unlikely(tp_len > size_max)) { - printk(KERN_ERR "packet size is too long (%d > %d)\n", - tp_len, size_max); + pr_err("packet size is too long (%d > %d)\n", tp_len, size_max); return -EMSGSIZE; } @@ -883,12 +882,11 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff * skb, NULL, tp_len); if (unlikely(err < 0)) return -EINVAL; - } else if (dev->hard_header_len ) { + } else if (dev->hard_header_len) { /* net device doesn't like empty head */ if (unlikely(tp_len <= dev->hard_header_len)) { - printk(KERN_ERR "packet size is too short " - "(%d < %d)\n", tp_len, - dev->hard_header_len); + pr_err("packet size is too short (%d < %d)\n", + tp_len, dev->hard_header_len); return -EINVAL; } @@ -917,9 +915,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff * skb, nr_frags = skb_shinfo(skb)->nr_frags; if (unlikely(nr_frags >= MAX_SKB_FRAGS)) { - printk(KERN_ERR "Packet exceed the number " - "of skb frags(%lu)\n", - MAX_SKB_FRAGS); + pr_err("Packet exceed the number of skb frags(%lu)\n", + MAX_SKB_FRAGS); return -EFAULT; } @@ -944,8 +941,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) struct net_device *dev; __be16 proto; int ifindex, err, reserve = 0; - void * ph; - struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name; + void *ph; + struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name; int tp_len, size_max; unsigned char *addr; int len_sum = 0; @@ -1038,8 +1035,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto out_xmit; packet_increment_head(&po->tx_ring); len_sum += tp_len; - } - while (likely((ph != NULL) || ((!(msg->msg_flags & MSG_DONTWAIT)) + } while (likely((ph != NULL) || ((!(msg->msg_flags & MSG_DONTWAIT)) && (atomic_read(&po->tx_ring.pending)))) ); @@ -1064,7 +1060,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; - struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name; + struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name; struct sk_buff *skb; struct net_device *dev; __be16 proto; @@ -1110,7 +1106,7 @@ static int packet_snd(struct socket *sock, skb = sock_alloc_send_skb(sk, len + LL_ALLOCATED_SPACE(dev), msg->msg_flags & MSG_DONTWAIT, &err); - if (skb==NULL) + if (skb == NULL) goto out_unlock; skb_reserve(skb, LL_RESERVED_SPACE(dev)); @@ -1122,7 +1118,7 @@ static int packet_snd(struct socket *sock, goto out_free; /* Returns -EFAULT on error */ - err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); if (err) goto out_free; @@ -1140,7 +1136,7 @@ static int packet_snd(struct socket *sock, dev_put(dev); - return(len); + return len; out_free: kfree_skb(skb); @@ -1283,9 +1279,10 @@ out_unlock: * Bind a packet socket to a device */ -static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int addr_len) +static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, + int addr_len) { - struct sock *sk=sock->sk; + struct sock *sk = sock->sk; char name[15]; struct net_device *dev; int err = -ENODEV; @@ -1296,7 +1293,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int add if (addr_len != sizeof(struct sockaddr)) return -EINVAL; - strlcpy(name,uaddr->sa_data,sizeof(name)); + strlcpy(name, uaddr->sa_data, sizeof(name)); dev = dev_get_by_name(sock_net(sk), name); if (dev) { @@ -1308,8 +1305,8 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int add static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { - struct sockaddr_ll *sll = (struct sockaddr_ll*)uaddr; - struct sock *sk=sock->sk; + struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr; + struct sock *sk = sock->sk; struct net_device *dev = NULL; int err; @@ -1404,7 +1401,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol) sk_add_node(sk, &net->packet.sklist); sock_prot_inuse_add(net, &packet_proto, 1); write_unlock_bh(&net->packet.sklist_lock); - return(0); + return 0; out: return err; } @@ -1441,7 +1438,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, * but then it will block. */ - skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err); + skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); /* * An error occurred so return it. Because skb_recv_datagram() @@ -1469,10 +1466,9 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, */ copied = skb->len; - if (copied > len) - { - copied=len; - msg->msg_flags|=MSG_TRUNC; + if (copied > len) { + copied = len; + msg->msg_flags |= MSG_TRUNC; } err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); @@ -1539,7 +1535,7 @@ static int packet_getname(struct socket *sock, struct sockaddr *uaddr, struct net_device *dev; struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); - struct sockaddr_ll *sll = (struct sockaddr_ll*)uaddr; + struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr; if (peer) return -EOPNOTSUPP; @@ -1584,14 +1580,15 @@ static int packet_dev_mc(struct net_device *dev, struct packet_mclist *i, else return dev_unicast_delete(dev, i->addr); break; - default:; + default: + break; } return 0; } static void packet_dev_mclist(struct net_device *dev, struct packet_mclist *i, int what) { - for ( ; i; i=i->next) { + for ( ; i; i = i->next) { if (i->ifindex == dev->ifindex) packet_dev_mc(dev, i, what); } @@ -1693,7 +1690,8 @@ static void packet_flush_mclist(struct sock *sk) struct net_device *dev; po->mclist = ml->next; - if ((dev = dev_get_by_index(sock_net(sk), ml->ifindex)) != NULL) { + dev = dev_get_by_index(sock_net(sk), ml->ifindex); + if (dev != NULL) { packet_dev_mc(dev, ml, -1); dev_put(dev); } @@ -1723,7 +1721,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return -EINVAL; if (len > sizeof(mreq)) len = sizeof(mreq); - if (copy_from_user(&mreq,optval,len)) + if (copy_from_user(&mreq, optval, len)) return -EFAULT; if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address))) return -EINVAL; @@ -1740,9 +1738,9 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv { struct tpacket_req req; - if (optlencopy_thresh = val; @@ -1985,51 +1983,51 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, struct sock *sk = sock->sk; switch (cmd) { - case SIOCOUTQ: - { - int amount = sk_wmem_alloc_get(sk); + case SIOCOUTQ: + { + int amount = sk_wmem_alloc_get(sk); - return put_user(amount, (int __user *)arg); - } - case SIOCINQ: - { - struct sk_buff *skb; - int amount = 0; - - spin_lock_bh(&sk->sk_receive_queue.lock); - skb = skb_peek(&sk->sk_receive_queue); - if (skb) - amount = skb->len; - spin_unlock_bh(&sk->sk_receive_queue.lock); - return put_user(amount, (int __user *)arg); - } - case SIOCGSTAMP: - return sock_get_timestamp(sk, (struct timeval __user *)arg); - case SIOCGSTAMPNS: - return sock_get_timestampns(sk, (struct timespec __user *)arg); + return put_user(amount, (int __user *)arg); + } + case SIOCINQ: + { + struct sk_buff *skb; + int amount = 0; + + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + if (skb) + amount = skb->len; + spin_unlock_bh(&sk->sk_receive_queue.lock); + return put_user(amount, (int __user *)arg); + } + case SIOCGSTAMP: + return sock_get_timestamp(sk, (struct timeval __user *)arg); + case SIOCGSTAMPNS: + return sock_get_timestampns(sk, (struct timespec __user *)arg); #ifdef CONFIG_INET - case SIOCADDRT: - case SIOCDELRT: - case SIOCDARP: - case SIOCGARP: - case SIOCSARP: - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCSIFFLAGS: - if (!net_eq(sock_net(sk), &init_net)) - return -ENOIOCTLCMD; - return inet_dgram_ops.ioctl(sock, cmd, arg); + case SIOCADDRT: + case SIOCDELRT: + case SIOCDARP: + case SIOCGARP: + case SIOCSARP: + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCSIFFLAGS: + if (!net_eq(sock_net(sk), &init_net)) + return -ENOIOCTLCMD; + return inet_dgram_ops.ioctl(sock, cmd, arg); #endif - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; } return 0; } @@ -2039,7 +2037,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, #define packet_poll datagram_poll #else -static unsigned int packet_poll(struct file * file, struct socket *sock, +static unsigned int packet_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; @@ -2069,7 +2067,7 @@ static unsigned int packet_poll(struct file * file, struct socket *sock, static void packet_mm_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; - struct socket * sock = file->private_data; + struct socket *sock = file->private_data; struct sock *sk = sock->sk; if (sk) @@ -2079,7 +2077,7 @@ static void packet_mm_open(struct vm_area_struct *vma) static void packet_mm_close(struct vm_area_struct *vma) { struct file *file = vma->vm_file; - struct socket * sock = file->private_data; + struct socket *sock = file->private_data; struct sock *sk = sock->sk; if (sk) @@ -2087,8 +2085,8 @@ static void packet_mm_close(struct vm_area_struct *vma) } static struct vm_operations_struct packet_mmap_ops = { - .open = packet_mm_open, - .close =packet_mm_close, + .open = packet_mm_open, + .close = packet_mm_close, }; static void free_pg_vec(char **pg_vec, unsigned int order, unsigned int len) @@ -2239,8 +2237,8 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, skb_queue_purge(rb_queue); #undef XC if (atomic_read(&po->mapped)) - printk(KERN_DEBUG "packet_mmap: vma is busy: %d\n", - atomic_read(&po->mapped)); + pr_err("packet_mmap: vma is busy: %d\n", + atomic_read(&po->mapped)); } mutex_unlock(&po->pg_vec_lock); @@ -2303,7 +2301,7 @@ static int packet_mmap(struct file *file, struct socket *sock, int pg_num; for (pg_num = 0; pg_num < rb->pg_vec_pages; - pg_num++,page++) { + pg_num++, page++) { err = vm_insert_page(vma, start, page); if (unlikely(err)) goto out; @@ -2372,7 +2370,7 @@ static struct net_proto_family packet_family_ops = { }; static struct notifier_block packet_netdev_notifier = { - .notifier_call =packet_notifier, + .notifier_call = packet_notifier, }; #ifdef CONFIG_PROC_FS @@ -2402,7 +2400,7 @@ static void *packet_seq_next(struct seq_file *seq, void *v, loff_t *pos) ++*pos; return (v == SEQ_START_TOKEN) ? sk_head(&net->packet.sklist) - : sk_next((struct sock*)v) ; + : sk_next((struct sock *)v) ; } static void packet_seq_stop(struct seq_file *seq, void *v) @@ -2430,7 +2428,7 @@ static int packet_seq_show(struct seq_file *seq, void *v) po->running, atomic_read(&s->sk_rmem_alloc), sock_i_uid(s), - sock_i_ino(s) ); + sock_i_ino(s)); } return 0; -- cgit v1.1 From 67edfef78639573e9b01c26295a935349aab6fa3 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 21 Jul 2009 23:00:40 +0000 Subject: TCP: Add comments to (near) all functions in tcp_output.c v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While looking for something else I spent some time adding one liner comments to the tcp_output.c functions that didn't have any. That makes the comments more consistent. I hope I documented everything right. No code changes. v2: Incorporated feedback from Ilpo. v3: Change style of one liner comments, add a few more comments. Signed-off-by: Andi Kleen Acked-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 58 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5bdf08d..c464892 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -59,6 +59,7 @@ int sysctl_tcp_base_mss __read_mostly = 512; /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; +/* Account for new data that has been sent to the network. */ static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); @@ -142,6 +143,7 @@ static void tcp_cwnd_restart(struct sock *sk, struct dst_entry *dst) tp->snd_cwnd_used = 0; } +/* Congestion state accounting after a packet has been sent. */ static void tcp_event_data_sent(struct tcp_sock *tp, struct sk_buff *skb, struct sock *sk) { @@ -161,6 +163,7 @@ static void tcp_event_data_sent(struct tcp_sock *tp, icsk->icsk_ack.pingpong = 1; } +/* Account for an ACK we sent. */ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts) { tcp_dec_quickack_mode(sk, pkts); @@ -276,6 +279,7 @@ static u16 tcp_select_window(struct sock *sk) return new_win; } +/* Packet ECN state for a SYN-ACK */ static inline void TCP_ECN_send_synack(struct tcp_sock *tp, struct sk_buff *skb) { TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_CWR; @@ -283,6 +287,7 @@ static inline void TCP_ECN_send_synack(struct tcp_sock *tp, struct sk_buff *skb) TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE; } +/* Packet ECN state for a SYN. */ static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); @@ -301,6 +306,9 @@ TCP_ECN_make_synack(struct request_sock *req, struct tcphdr *th) th->ece = 1; } +/* Set up ECN state for a packet on a ESTABLISHED socket that is about to + * be sent. + */ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, int tcp_header_len) { @@ -362,7 +370,9 @@ struct tcp_out_options { __u32 tsval, tsecr; /* need to include OPTION_TS */ }; -/* Beware: Something in the Internet is very sensitive to the ordering of +/* Write previously computed TCP options to the packet. + * + * Beware: Something in the Internet is very sensitive to the ordering of * TCP options, we learned this through the hard way, so be careful here. * Luckily we can at least blame others for their non-compliance but from * inter-operatibility perspective it seems that we're somewhat stuck with @@ -445,6 +455,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, } } +/* Compute TCP options for SYN packets. This is not the final + * network wire format yet. + */ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5) { @@ -493,6 +506,7 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, return size; } +/* Set up TCP options for SYN-ACKs. */ static unsigned tcp_synack_options(struct sock *sk, struct request_sock *req, unsigned mss, struct sk_buff *skb, @@ -541,6 +555,9 @@ static unsigned tcp_synack_options(struct sock *sk, return size; } +/* Compute TCP options for ESTABLISHED sockets. This is not the + * final wire format yet. + */ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5) { @@ -705,7 +722,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, return net_xmit_eval(err); } -/* This routine just queue's the buffer +/* This routine just queues the buffer for sending. * * NOTE: probe0 timer is not checked, do not forget tcp_push_pending_frames, * otherwise socket can stall. @@ -722,6 +739,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) sk_mem_charge(sk, skb->truesize); } +/* Initialize TSO segments for a packet. */ static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now) { @@ -909,6 +927,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) skb->len = skb->data_len; } +/* Remove acked data from a packet in the transmit queue. */ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) { if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) @@ -937,7 +956,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) return 0; } -/* Not accounting for SACKs here. */ +/* Calculate MSS. Not accounting for SACKs here. */ int tcp_mtu_to_mss(struct sock *sk, int pmtu) { struct tcp_sock *tp = tcp_sk(sk); @@ -981,6 +1000,7 @@ int tcp_mss_to_mtu(struct sock *sk, int mss) return mtu; } +/* MTU probing init per socket */ void tcp_mtup_init(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -1143,7 +1163,8 @@ static inline unsigned int tcp_cwnd_test(struct tcp_sock *tp, return 0; } -/* This must be invoked the first time we consider transmitting +/* Intialize TSO state of a skb. + * This must be invoked the first time we consider transmitting * SKB onto the wire. */ static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb, @@ -1158,6 +1179,7 @@ static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb, return tso_segs; } +/* Minshall's variant of the Nagle send check. */ static inline int tcp_minshall_check(const struct tcp_sock *tp) { return after(tp->snd_sml, tp->snd_una) && @@ -1242,6 +1264,7 @@ static unsigned int tcp_snd_test(struct sock *sk, struct sk_buff *skb, return cwnd_quota; } +/* Test if sending is allowed right now. */ int tcp_may_send_now(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -1378,6 +1401,10 @@ send_now: } /* Create a new MTU probe if we are ready. + * MTU probe is regularly attempting to increase the path MTU by + * deliberately sending larger packets. This discovers routing + * changes resulting in larger path MTUs. + * * Returns 0 if we should wait to probe (no cwnd available), * 1 if a probe was sent, * -1 otherwise @@ -1790,6 +1817,7 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) sk_wmem_free_skb(sk, next_skb); } +/* Check if coalescing SKBs is legal. */ static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb) { if (tcp_skb_pcount(skb) > 1) @@ -1808,6 +1836,9 @@ static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb) return 1; } +/* Collapse packets in the retransmit queue to make to create + * less packets on the wire. This is only done on retransmission. + */ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, int space) { @@ -1957,6 +1988,9 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) return err; } +/* Check if we forward retransmits are possible in the current + * window/congestion state. + */ static int tcp_can_forward_retransmit(struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); @@ -2145,7 +2179,8 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority) TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS); } -/* WARNING: This routine must only be called when we have already sent +/* Send a crossed SYN-ACK during socket establishment. + * WARNING: This routine must only be called when we have already sent * a SYN packet that crossed the incoming SYN that caused this routine * to get called. If this assumption fails then the initial rcv_wnd * and rcv_wscale values will not be correct. @@ -2180,9 +2215,7 @@ int tcp_send_synack(struct sock *sk) return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); } -/* - * Prepare a SYN-ACK. - */ +/* Prepare a SYN-ACK. */ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req) { @@ -2269,9 +2302,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, return skb; } -/* - * Do all connect socket setups that can be done AF independent. - */ +/* Do all connect socket setups that can be done AF independent. */ static void tcp_connect_init(struct sock *sk) { struct dst_entry *dst = __sk_dst_get(sk); @@ -2330,9 +2361,7 @@ static void tcp_connect_init(struct sock *sk) tcp_clear_retrans(tp); } -/* - * Build a SYN and send it off. - */ +/* Build a SYN and send it off. */ int tcp_connect(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -2493,6 +2522,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent) return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC); } +/* Initiate keepalive or window probe from timer. */ int tcp_write_wakeup(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); -- cgit v1.1 From 99783e2cde6eccbd31efeb03a79f26bb5f239c36 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 03:54:43 +0200 Subject: mac80211: fix sparse warning ieee80211_testmode_cmd can very well be static. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 36f8f24..52928ad 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1293,7 +1293,7 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy) } #ifdef CONFIG_NL80211_TESTMODE -int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) +static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) { struct ieee80211_local *local = wiphy_priv(wiphy); -- cgit v1.1 From 0a2b8bb24d4eb67788edd71d1ef8aa86c2e17e0f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 13:46:22 +0200 Subject: mac80211: driver operation debugging This makes mac80211 use the event tracing framework to log all operations as given to the driver. This will need to be extended with more information, but as a start it should be good. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 12 + net/mac80211/Makefile | 3 + net/mac80211/driver-ops.h | 85 ++++-- net/mac80211/driver-trace.c | 6 + net/mac80211/driver-trace.h | 648 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 732 insertions(+), 22 deletions(-) create mode 100644 net/mac80211/driver-trace.c create mode 100644 net/mac80211/driver-trace.h (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 182c9c5..19a4c66 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -206,3 +206,15 @@ config MAC80211_DEBUG_COUNTERS and show them in debugfs. If unsure, say N. + +config MAC80211_DRIVER_API_TRACER + bool "Driver API tracer" + depends on MAC80211_DEBUG_MENU + depends on EVENT_TRACING + help + Say Y here to make mac80211 register with the ftrace + framework for the driver API -- you can see which + driver methods it is calling then by looking at the + trace. + + If unsure, say N. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 0e3ab88..91284a7 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -41,6 +41,9 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ mac80211-$(CONFIG_PM) += pm.o +mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o +CFLAGS_driver-trace.o := -I$(src) + # objects for PID algorithm rc80211_pid-y := rc80211_pid_algo.o rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b13446a..4100c36 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -3,6 +3,7 @@ #include #include "ieee80211_i.h" +#include "driver-trace.h" static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) { @@ -11,29 +12,37 @@ static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) static inline int drv_start(struct ieee80211_local *local) { - return local->ops->start(&local->hw); + int ret = local->ops->start(&local->hw); + trace_drv_start(local, ret); + return ret; } static inline void drv_stop(struct ieee80211_local *local) { local->ops->stop(&local->hw); + trace_drv_stop(local); } static inline int drv_add_interface(struct ieee80211_local *local, struct ieee80211_if_init_conf *conf) { - return local->ops->add_interface(&local->hw, conf); + int ret = local->ops->add_interface(&local->hw, conf); + trace_drv_add_interface(local, conf->mac_addr, conf->vif, ret); + return ret; } static inline void drv_remove_interface(struct ieee80211_local *local, struct ieee80211_if_init_conf *conf) { local->ops->remove_interface(&local->hw, conf); + trace_drv_remove_interface(local, conf->mac_addr, conf->vif); } static inline int drv_config(struct ieee80211_local *local, u32 changed) { - return local->ops->config(&local->hw, changed); + int ret = local->ops->config(&local->hw, changed); + trace_drv_config(local, changed, ret); + return ret; } static inline void drv_bss_info_changed(struct ieee80211_local *local, @@ -43,6 +52,7 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { if (local->ops->bss_info_changed) local->ops->bss_info_changed(&local->hw, vif, info, changed); + trace_drv_bss_info_changed(local, vif, info, changed); } static inline void drv_configure_filter(struct ieee80211_local *local, @@ -53,14 +63,18 @@ static inline void drv_configure_filter(struct ieee80211_local *local, { local->ops->configure_filter(&local->hw, changed_flags, total_flags, mc_count, mc_list); + trace_drv_configure_filter(local, changed_flags, total_flags, + mc_count); } static inline int drv_set_tim(struct ieee80211_local *local, struct ieee80211_sta *sta, bool set) { + int ret = 0; if (local->ops->set_tim) - return local->ops->set_tim(&local->hw, sta, set); - return 0; + ret = local->ops->set_tim(&local->hw, sta, set); + trace_drv_set_tim(local, sta, set, ret); + return ret; } static inline int drv_set_key(struct ieee80211_local *local, @@ -68,7 +82,9 @@ static inline int drv_set_key(struct ieee80211_local *local, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { - return local->ops->set_key(&local->hw, cmd, vif, sta, key); + int ret = local->ops->set_key(&local->hw, cmd, vif, sta, key); + trace_drv_set_key(local, cmd, vif, sta, key, ret); + return ret; } static inline void drv_update_tkip_key(struct ieee80211_local *local, @@ -79,32 +95,41 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, if (local->ops->update_tkip_key) local->ops->update_tkip_key(&local->hw, conf, address, iv32, phase1key); + trace_drv_update_tkip_key(local, conf, address, iv32); } static inline int drv_hw_scan(struct ieee80211_local *local, struct cfg80211_scan_request *req) { - return local->ops->hw_scan(&local->hw, req); + int ret = local->ops->hw_scan(&local->hw, req); + trace_drv_hw_scan(local, req, ret); + return ret; } static inline void drv_sw_scan_start(struct ieee80211_local *local) { if (local->ops->sw_scan_start) local->ops->sw_scan_start(&local->hw); + trace_drv_sw_scan_start(local); } static inline void drv_sw_scan_complete(struct ieee80211_local *local) { if (local->ops->sw_scan_complete) local->ops->sw_scan_complete(&local->hw); + trace_drv_sw_scan_complete(local); } static inline int drv_get_stats(struct ieee80211_local *local, struct ieee80211_low_level_stats *stats) { - if (!local->ops->get_stats) - return -EOPNOTSUPP; - return local->ops->get_stats(&local->hw, stats); + int ret = -EOPNOTSUPP; + + if (local->ops->get_stats) + ret = local->ops->get_stats(&local->hw, stats); + trace_drv_get_stats(local, stats, ret); + + return ret; } static inline void drv_get_tkip_seq(struct ieee80211_local *local, @@ -112,14 +137,17 @@ static inline void drv_get_tkip_seq(struct ieee80211_local *local, { if (local->ops->get_tkip_seq) local->ops->get_tkip_seq(&local->hw, hw_key_idx, iv32, iv16); + trace_drv_get_tkip_seq(local, hw_key_idx, iv32, iv16); } static inline int drv_set_rts_threshold(struct ieee80211_local *local, u32 value) { + int ret = 0; if (local->ops->set_rts_threshold) - return local->ops->set_rts_threshold(&local->hw, value); - return 0; + ret = local->ops->set_rts_threshold(&local->hw, value); + trace_drv_set_rts_threshold(local, value, ret); + return ret; } static inline void drv_sta_notify(struct ieee80211_local *local, @@ -129,46 +157,57 @@ static inline void drv_sta_notify(struct ieee80211_local *local, { if (local->ops->sta_notify) local->ops->sta_notify(&local->hw, vif, cmd, sta); + trace_drv_sta_notify(local, vif, cmd, sta); } static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue, const struct ieee80211_tx_queue_params *params) { + int ret = -EOPNOTSUPP; if (local->ops->conf_tx) - return local->ops->conf_tx(&local->hw, queue, params); - return -EOPNOTSUPP; + ret = local->ops->conf_tx(&local->hw, queue, params); + trace_drv_conf_tx(local, queue, params, ret); + return ret; } static inline int drv_get_tx_stats(struct ieee80211_local *local, struct ieee80211_tx_queue_stats *stats) { - return local->ops->get_tx_stats(&local->hw, stats); + int ret = local->ops->get_tx_stats(&local->hw, stats); + trace_drv_get_tx_stats(local, stats, ret); + return ret; } static inline u64 drv_get_tsf(struct ieee80211_local *local) { + u64 ret = -1ULL; if (local->ops->get_tsf) - return local->ops->get_tsf(&local->hw); - return -1ULL; + ret = local->ops->get_tsf(&local->hw); + trace_drv_get_tsf(local, ret); + return ret; } static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf) { if (local->ops->set_tsf) local->ops->set_tsf(&local->hw, tsf); + trace_drv_set_tsf(local, tsf); } static inline void drv_reset_tsf(struct ieee80211_local *local) { if (local->ops->reset_tsf) local->ops->reset_tsf(&local->hw); + trace_drv_reset_tsf(local); } static inline int drv_tx_last_beacon(struct ieee80211_local *local) { + int ret = 1; if (local->ops->tx_last_beacon) - return local->ops->tx_last_beacon(&local->hw); - return 1; + ret = local->ops->tx_last_beacon(&local->hw); + trace_drv_tx_last_beacon(local, ret); + return ret; } static inline int drv_ampdu_action(struct ieee80211_local *local, @@ -176,10 +215,12 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { + int ret = -EOPNOTSUPP; if (local->ops->ampdu_action) - return local->ops->ampdu_action(&local->hw, action, - sta, tid, ssn); - return -EOPNOTSUPP; + ret = local->ops->ampdu_action(&local->hw, action, + sta, tid, ssn); + trace_drv_ampdu_action(local, action, sta, tid, ssn, ret); + return ret; } diff --git a/net/mac80211/driver-trace.c b/net/mac80211/driver-trace.c new file mode 100644 index 0000000..6da6f79 --- /dev/null +++ b/net/mac80211/driver-trace.c @@ -0,0 +1,6 @@ +/* bug in tracepoint.h, it should include this */ +#include + +#include "driver-ops.h" +#define CREATE_TRACE_POINTS +#include "driver-trace.h" diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h new file mode 100644 index 0000000..48c93d1 --- /dev/null +++ b/net/mac80211/driver-trace.h @@ -0,0 +1,648 @@ +#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __MAC80211_DRIVER_TRACE + +#include +#include +#include "ieee80211_i.h" + +#ifndef CONFIG_MAC80211_DRIVER_API_TRACER +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mac80211 + +#define MAXNAME 32 +#define LOCAL_ENTRY __array(char, wiphy_name, 32) +#define LOCAL_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(local->hw.wiphy), MAXNAME) +#define LOCAL_PR_FMT "%s" +#define LOCAL_PR_ARG __entry->wiphy_name + +#define STA_ENTRY __array(char, sta_addr, ETH_ALEN) +#define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) +#define STA_PR_FMT " sta:%pM" +#define STA_PR_ARG __entry->sta_addr + +#define VIF_ENTRY __field(enum nl80211_iftype, vif_type) __field(void *, vif) +#define VIF_ASSIGN __entry->vif_type = vif ? vif->type : 0; __entry->vif = vif +#define VIF_PR_FMT " vif:%p(%d)" +#define VIF_PR_ARG __entry->vif, __entry->vif_type + +TRACE_EVENT(drv_start, + TP_PROTO(struct ieee80211_local *local, int ret), + + TP_ARGS(local, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_stop, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_add_interface, + TP_PROTO(struct ieee80211_local *local, + const u8 *addr, + struct ieee80211_vif *vif, + int ret), + + TP_ARGS(local, addr, vif, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, addr, 6) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->addr, addr, 6); + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " addr:%pM ret:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr, __entry->ret + ) +); + +TRACE_EVENT(drv_remove_interface, + TP_PROTO(struct ieee80211_local *local, + const u8 *addr, struct ieee80211_vif *vif), + + TP_ARGS(local, addr, vif), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, addr, 6) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->addr, addr, 6); + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr + ) +); + +TRACE_EVENT(drv_config, + TP_PROTO(struct ieee80211_local *local, + u32 changed, + int ret), + + TP_ARGS(local, changed, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, changed) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->changed = changed; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " ch:%#x ret:%d", + LOCAL_PR_ARG, __entry->changed, __entry->ret + ) +); + +TRACE_EVENT(drv_bss_info_changed, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed), + + TP_ARGS(local, vif, info, changed), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(bool, assoc) + __field(u16, aid) + __field(bool, cts) + __field(bool, shortpre) + __field(bool, shortslot) + __field(u8, dtimper) + __field(u16, bcnint) + __field(u16, assoc_cap) + __field(u64, timestamp) + __field(u32, basic_rates) + __field(u32, changed) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->changed = changed; + __entry->aid = info->aid; + __entry->assoc = info->assoc; + __entry->shortpre = info->use_short_preamble; + __entry->cts = info->use_cts_prot; + __entry->shortslot = info->use_short_slot; + __entry->dtimper = info->dtim_period; + __entry->bcnint = info->beacon_int; + __entry->assoc_cap = info->assoc_capability; + __entry->timestamp = info->timestamp; + __entry->basic_rates = info->basic_rates; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " changed:%#x", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed + ) +); + +TRACE_EVENT(drv_configure_filter, + TP_PROTO(struct ieee80211_local *local, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count), + + TP_ARGS(local, changed_flags, total_flags, mc_count), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(unsigned int, changed) + __field(unsigned int, total) + __field(int, mc) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->changed = changed_flags; + __entry->total = *total_flags; + __entry->mc = mc_count; + ), + + TP_printk( + LOCAL_PR_FMT " changed:%#x total:%#x mc:%d", + LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc + ) +); + +TRACE_EVENT(drv_set_tim, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, bool set, int ret), + + TP_ARGS(local, sta, set, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(bool, set) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->set = set; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT " set:%d ret:%d", + LOCAL_PR_ARG, STA_PR_FMT, __entry->set, __entry->ret + ) +); + +TRACE_EVENT(drv_set_key, + TP_PROTO(struct ieee80211_local *local, + enum set_key_cmd cmd, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int ret), + + TP_ARGS(local, cmd, vif, sta, key, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(enum ieee80211_key_alg, alg) + __field(u8, hw_key_idx) + __field(u8, flags) + __field(s8, keyidx) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->alg = key->alg; + __entry->flags = key->flags; + __entry->keyidx = key->keyidx; + __entry->hw_key_idx = key->hw_key_idx; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ret:%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret + ) +); + +TRACE_EVENT(drv_update_tkip_key, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_key_conf *conf, + const u8 *address, u32 iv32), + + TP_ARGS(local, conf, address, iv32), + + TP_STRUCT__entry( + LOCAL_ENTRY + __array(u8, addr, 6) + __field(u32, iv32) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + memcpy(__entry->addr, address, 6); + __entry->iv32 = iv32; + ), + + TP_printk( + LOCAL_PR_FMT " addr:%pM iv32:%#x", + LOCAL_PR_ARG, __entry->addr, __entry->iv32 + ) +); + +TRACE_EVENT(drv_hw_scan, + TP_PROTO(struct ieee80211_local *local, + struct cfg80211_scan_request *req, int ret), + + TP_ARGS(local, req, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " ret:%d", + LOCAL_PR_ARG, __entry->ret + ) +); + +TRACE_EVENT(drv_sw_scan_start, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_sw_scan_complete, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_get_stats, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_low_level_stats *stats, + int ret), + + TP_ARGS(local, stats, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, ret) + __field(unsigned int, ackfail) + __field(unsigned int, rtsfail) + __field(unsigned int, fcserr) + __field(unsigned int, rtssucc) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + __entry->ackfail = stats->dot11ACKFailureCount; + __entry->rtsfail = stats->dot11RTSFailureCount; + __entry->fcserr = stats->dot11FCSErrorCount; + __entry->rtssucc = stats->dot11RTSSuccessCount; + ), + + TP_printk( + LOCAL_PR_FMT " ret:%d", + LOCAL_PR_ARG, __entry->ret + ) +); + +TRACE_EVENT(drv_get_tkip_seq, + TP_PROTO(struct ieee80211_local *local, + u8 hw_key_idx, u32 *iv32, u16 *iv16), + + TP_ARGS(local, hw_key_idx, iv32, iv16), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u8, hw_key_idx) + __field(u32, iv32) + __field(u16, iv16) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->hw_key_idx = hw_key_idx; + __entry->iv32 = *iv32; + __entry->iv16 = *iv16; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_set_rts_threshold, + TP_PROTO(struct ieee80211_local *local, u32 value, int ret), + + TP_ARGS(local, value, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, value) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + __entry->value = value; + ), + + TP_printk( + LOCAL_PR_FMT " value:%d ret:%d", + LOCAL_PR_ARG, __entry->value, __entry->ret + ) +); + +TRACE_EVENT(drv_sta_notify, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta), + + TP_ARGS(local, vif, cmd, sta), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u32, cmd) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->cmd = cmd; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " cmd:%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->cmd + ) +); + +TRACE_EVENT(drv_conf_tx, + TP_PROTO(struct ieee80211_local *local, u16 queue, + const struct ieee80211_tx_queue_params *params, + int ret), + + TP_ARGS(local, queue, params, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u16, queue) + __field(u16, txop) + __field(u16, cw_min) + __field(u16, cw_max) + __field(u8, aifs) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->queue = queue; + __entry->ret = ret; + __entry->txop = params->txop; + __entry->cw_max = params->cw_max; + __entry->cw_min = params->cw_min; + __entry->aifs = params->aifs; + ), + + TP_printk( + LOCAL_PR_FMT " queue:%d ret:%d", + LOCAL_PR_ARG, __entry->queue, __entry->ret + ) +); + +TRACE_EVENT(drv_get_tx_stats, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_tx_queue_stats *stats, + int ret), + + TP_ARGS(local, stats, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " ret:%d", + LOCAL_PR_ARG, __entry->ret + ) +); + +TRACE_EVENT(drv_get_tsf, + TP_PROTO(struct ieee80211_local *local, u64 ret), + + TP_ARGS(local, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u64, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " ret:%llu", + LOCAL_PR_ARG, (unsigned long long)__entry->ret + ) +); + +TRACE_EVENT(drv_set_tsf, + TP_PROTO(struct ieee80211_local *local, u64 tsf), + + TP_ARGS(local, tsf), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u64, tsf) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->tsf = tsf; + ), + + TP_printk( + LOCAL_PR_FMT " tsf:%llu", + LOCAL_PR_ARG, (unsigned long long)__entry->tsf + ) +); + +TRACE_EVENT(drv_reset_tsf, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(drv_tx_last_beacon, + TP_PROTO(struct ieee80211_local *local, int ret), + + TP_ARGS(local, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " ret:%d", + LOCAL_PR_ARG, __entry->ret + ) +); + +TRACE_EVENT(drv_ampdu_action, + TP_PROTO(struct ieee80211_local *local, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, + u16 *ssn, int ret), + + TP_ARGS(local, action, sta, tid, ssn, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u32, action) + __field(u16, tid) + __field(u16, ssn) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->ret = ret; + __entry->action = action; + __entry->tid = tid; + __entry->ssn = *ssn; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT " action:%d tid:%d ret:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret + ) +); +#endif /* __MAC80211_DRIVER_TRACE */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE driver-trace +#include -- cgit v1.1 From a71d62dbf3f0523b7a456333196cb26cf783fe92 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 23:41:27 +0200 Subject: cfg80211: fix race in giwrate cfg80211_wext_giwrate doesn't lock the wdev, so it cannot access current_bss race-free. Also, there's little point in trying to ask the driver for an AP that it never told us about, so avoid that case. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 9d101d5..5088d89 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1127,7 +1127,7 @@ int cfg80211_wext_giwrate(struct net_device *dev, struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); /* we are under RTNL - globally locked - so can use a static struct */ static struct station_info sinfo; - u8 *addr; + u8 addr[ETH_ALEN]; int err; if (wdev->iftype != NL80211_IFTYPE_STATION) @@ -1136,12 +1136,15 @@ int cfg80211_wext_giwrate(struct net_device *dev, if (!rdev->ops->get_station) return -EOPNOTSUPP; + err = 0; + wdev_lock(wdev); if (wdev->current_bss) - addr = wdev->current_bss->pub.bssid; - else if (wdev->wext.connect.bssid) - addr = wdev->wext.connect.bssid; + memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN); else - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + wdev_unlock(wdev); + if (err) + return err; err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); if (err) -- cgit v1.1 From 4bde0f7d1dca0a7d886997eb8dee3fb47a6484e4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 7 Jul 2009 23:46:51 +0200 Subject: cfg80211: fix two buglets This fixes two small bugs: 1) the connect variable is already initialised, and the assignment to auth_type overwrites the previous setting with a wrong value 2) when all authentication attempts fail, we need to report that we couldn't connect Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 4 ---- net/wireless/sme.c | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4478760..a00fd64 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3680,10 +3680,6 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) wiphy = &rdev->wiphy; - connect.bssid = NULL; - connect.channel = NULL; - connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - if (info->attrs[NL80211_ATTR_MAC]) connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index df9173f..79ca56c 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -295,9 +295,8 @@ void cfg80211_sme_rx_auth(struct net_device *dev, wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { - wdev->sme_state = CFG80211_SME_IDLE; - kfree(wdev->conn); - wdev->conn = NULL; + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + status_code, false); } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; -- cgit v1.1 From 10c836d7896e9d7b683a76f3cac3c289d8da72ef Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 9 Jul 2009 14:42:16 -0700 Subject: mac80211: Assign next hop address to pending mesh frames Assign next hop address to pending mesh frames once the path is resolved. Regression. Frames transmitted when a mesh path was wating to be resolved were being transmitted with an invalid Receiver Address. [Changes since v1] Suggested by Johannes: - Improved frame_queue traversal - Narower RCU scope Signed-off-by: Javier Cardona Signed-off-by: Andrey Yurovsky Signed-off-by: John W. Linville --- net/mac80211/mesh_pathtbl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'net') diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 479597e..f0304bf 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -55,7 +55,25 @@ static DEFINE_RWLOCK(pathtbl_resize_lock); */ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) { + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct sk_buff_head tmpq; + unsigned long flags; + rcu_assign_pointer(mpath->next_hop, sta); + + __skb_queue_head_init(&tmpq); + + spin_lock_irqsave(&mpath->frame_queue.lock, flags); + + while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { + hdr = (struct ieee80211_hdr *) skb->data; + memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); + __skb_queue_tail(&tmpq, skb); + } + + skb_queue_splice(&tmpq, &mpath->frame_queue); + spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); } -- cgit v1.1 From b9454e83cac42fcdc90bfbfba479132bd6629455 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jul 2009 13:29:08 +0200 Subject: nl80211: introduce new key attributes We will soon want to nest key attributes into some new attribute for configuring static WEP keys at connect() and ibss_join() time, so we need nested attributes for that. However, key attributes right now are 'global'. This patch thus introduces new nested attributes for the key settings and functions for parsing them. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 203 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 156 insertions(+), 47 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a00fd64..50cf593 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -73,6 +73,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, @@ -134,6 +135,18 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, }; +/* policy for the attributes */ +static struct nla_policy +nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { + [NL80211_KEY_DATA] = { .type = NLA_BINARY, + .len = WLAN_MAX_KEY_LEN }, + [NL80211_KEY_IDX] = { .type = NLA_U8 }, + [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, + [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, + [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, + [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, +}; + /* IE validation */ static bool is_valid_ie_attr(const struct nlattr *attr) { @@ -198,6 +211,100 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, /* netlink command implementations */ +struct key_parse { + struct key_params p; + int idx; + bool def, defmgmt; +}; + +static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) +{ + struct nlattr *tb[NL80211_KEY_MAX + 1]; + int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, + nl80211_key_policy); + if (err) + return err; + + k->def = !!tb[NL80211_KEY_DEFAULT]; + k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; + + if (tb[NL80211_KEY_IDX]) + k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); + + if (tb[NL80211_KEY_DATA]) { + k->p.key = nla_data(tb[NL80211_KEY_DATA]); + k->p.key_len = nla_len(tb[NL80211_KEY_DATA]); + } + + if (tb[NL80211_KEY_SEQ]) { + k->p.seq = nla_data(tb[NL80211_KEY_SEQ]); + k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]); + } + + if (tb[NL80211_KEY_CIPHER]) + k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); + + return 0; +} + +static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) +{ + if (info->attrs[NL80211_ATTR_KEY_DATA]) { + k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); + k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); + } + + if (info->attrs[NL80211_ATTR_KEY_SEQ]) { + k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); + k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); + } + + if (info->attrs[NL80211_ATTR_KEY_IDX]) + k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); + + if (info->attrs[NL80211_ATTR_KEY_CIPHER]) + k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); + + k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; + k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; + + return 0; +} + +static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) +{ + int err; + + memset(k, 0, sizeof(*k)); + k->idx = -1; + + if (info->attrs[NL80211_ATTR_KEY]) + err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); + else + err = nl80211_parse_key_old(info, k); + + if (err) + return err; + + if (k->def && k->defmgmt) + return -EINVAL; + + if (k->idx != -1) { + if (k->defmgmt) { + if (k->idx < 4 || k->idx > 5) + return -EINVAL; + } else if (k->def) { + if (k->idx < 0 || k->idx > 3) + return -EINVAL; + } else { + if (k->idx < 0 || k->idx > 5) + return -EINVAL; + } + } + + return 0; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -943,10 +1050,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) struct get_key_cookie { struct sk_buff *msg; int error; + int idx; }; static void get_key_callback(void *c, struct key_params *params) { + struct nlattr *key; struct get_key_cookie *cookie = c; if (params->key) @@ -961,6 +1070,26 @@ static void get_key_callback(void *c, struct key_params *params) NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, params->cipher); + key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY); + if (!key) + goto nla_put_failure; + + if (params->key) + NLA_PUT(cookie->msg, NL80211_KEY_DATA, + params->key_len, params->key); + + if (params->seq) + NLA_PUT(cookie->msg, NL80211_KEY_SEQ, + params->seq_len, params->seq); + + if (params->cipher) + NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER, + params->cipher); + + NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx); + + nla_nest_end(cookie->msg, key); + return; nla_put_failure: cookie->error = 1; @@ -1014,6 +1143,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) } cookie.msg = msg; + cookie.idx = key_idx; NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); @@ -1049,26 +1179,21 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; + struct key_parse key; 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]); + err = nl80211_parse_key(info, &key); + if (err) + return err; - if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { - if (key_idx < 4 || key_idx > 5) - return -EINVAL; - } else if (key_idx > 3) + if (key.idx < 0) return -EINVAL; - /* currently only support setting default key */ - if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && - !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) + /* only support setting default key */ + if (!key.def && !key.defmgmt) return -EINVAL; rtnl_lock(); @@ -1077,7 +1202,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto unlock_rtnl; - if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) + if (key.def) func = rdev->ops->set_default_key; else func = rdev->ops->set_default_mgmt_key; @@ -1087,13 +1212,13 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = func(&rdev->wiphy, dev, key_idx); + err = func(&rdev->wiphy, dev, key.idx); #ifdef CONFIG_WIRELESS_EXT if (!err) { if (func == rdev->ops->set_default_key) - dev->ieee80211_ptr->wext.default_key = key_idx; + dev->ieee80211_ptr->wext.default_key = key.idx; else - dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; + dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; } #endif @@ -1112,34 +1237,20 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; int err, i; struct net_device *dev; - struct key_params params; - u8 key_idx = 0; + struct key_parse key; u8 *mac_addr = NULL; - memset(¶ms, 0, sizeof(params)); + err = nl80211_parse_key(info, &key); + if (err) + return err; - if (!info->attrs[NL80211_ATTR_KEY_CIPHER]) + if (!key.p.key) return -EINVAL; - if (info->attrs[NL80211_ATTR_KEY_DATA]) { - params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); - params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); - } - - if (info->attrs[NL80211_ATTR_KEY_SEQ]) { - params.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); - params.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); - } - - if (info->attrs[NL80211_ATTR_KEY_IDX]) - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); - if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (cfg80211_validate_key_settings(¶ms, key_idx, mac_addr)) + if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) return -EINVAL; rtnl_lock(); @@ -1149,7 +1260,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) goto unlock_rtnl; for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) - if (params.cipher == rdev->wiphy.cipher_suites[i]) + if (key.p.cipher == rdev->wiphy.cipher_suites[i]) break; if (i == rdev->wiphy.n_cipher_suites) { err = -EINVAL; @@ -1161,7 +1272,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, ¶ms); + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); out: cfg80211_unlock_rdev(rdev); @@ -1177,14 +1288,12 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; int err; struct net_device *dev; - u8 key_idx = 0; u8 *mac_addr = NULL; + struct key_parse key; - if (info->attrs[NL80211_ATTR_KEY_IDX]) - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (key_idx > 5) - return -EINVAL; + err = nl80211_parse_key(info, &key); + if (err) + return err; if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -1200,13 +1309,13 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->del_key(&rdev->wiphy, dev, key_idx, mac_addr); + err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { - if (key_idx == dev->ieee80211_ptr->wext.default_key) + if (key.idx == dev->ieee80211_ptr->wext.default_key) dev->ieee80211_ptr->wext.default_key = -1; - else if (key_idx == dev->ieee80211_ptr->wext.default_mgmt_key) + else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key) dev->ieee80211_ptr->wext.default_mgmt_key = -1; } #endif -- cgit v1.1 From fffd0934b9390f34bec45762192b7edd3b12b4b5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jul 2009 14:22:54 +0200 Subject: cfg80211: rework key operation This reworks the key operation in cfg80211, and now only allows, from userspace, configuring keys (via nl80211) after the connection has been established (in managed mode), the IBSS been joined (in IBSS mode), at any time (in AP[_VLAN] modes) or never for all the other modes. In order to do shared key authentication correctly, it is now possible to give a WEP key to the AUTH command. To configure static WEP keys, these are given to the CONNECT or IBSS_JOIN command directly, for a userspace SME it is assumed it will configure it properly after the connection has been established. Since mac80211 used to check the default key in IBSS mode to see whether or not the network is protected, it needs an update in that area, as well as an update to make use of the WEP key passed to auth() for shared key authentication. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 9 +-- net/mac80211/ieee80211_i.h | 8 ++- net/mac80211/mlme.c | 11 ++- net/mac80211/util.c | 16 +++-- net/mac80211/wep.c | 6 +- net/mac80211/wep.h | 3 + net/wireless/core.c | 11 ++- net/wireless/core.h | 32 +++++++-- net/wireless/ibss.c | 79 +++++++++++++++++---- net/wireless/mlme.c | 16 ++++- net/wireless/nl80211.c | 170 ++++++++++++++++++++++++++++++++++++++++----- net/wireless/sme.c | 97 +++++++++++++++++++------- net/wireless/util.c | 41 ++++++++++- net/wireless/wext-compat.c | 163 ++++++++++++++++++++++++++----------------- net/wireless/wext-sme.c | 30 ++++++-- 15 files changed, 532 insertions(+), 160 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 15d5a53..8e22200 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -57,7 +57,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, */ if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, - sdata->u.ibss.bssid, 0); + sdata->u.ibss.bssid, NULL, 0, 0); } static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, @@ -494,7 +494,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) capability = WLAN_CAPABILITY_IBSS; - if (sdata->default_key) + if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; else sdata->drop_unencrypted = 0; @@ -524,9 +524,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) return; capability = WLAN_CAPABILITY_IBSS; - if (sdata->default_key) + if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; - if (ifibss->fixed_bssid) bssid = ifibss->bssid; if (ifibss->fixed_channel) @@ -872,6 +871,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, } else sdata->u.ibss.fixed_bssid = false; + sdata->u.ibss.privacy = params->privacy; + sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->u.ibss.channel = params->channel; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 327aabc..06b3411 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -247,6 +247,9 @@ struct ieee80211_mgd_work { int tries; + u8 key[WLAN_KEY_LEN_WEP104]; + u8 key_len, key_idx; + /* must be last */ u8 ie[0]; /* for auth or assoc frame, not probe */ }; @@ -321,6 +324,7 @@ struct ieee80211_if_ibss { bool fixed_bssid; bool fixed_channel; + bool privacy; u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -1093,8 +1097,8 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, - u8 *extra, size_t extra_len, - const u8 *bssid, int encrypt); + u8 *extra, size_t extra_len, const u8 *bssid, + const u8 *key, u8 key_len, u8 key_idx); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie, size_t ie_len); void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c9db964..8e4a604 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -954,7 +954,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, sdata->dev->name, wk->bss->cbss.bssid, wk->tries); ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len, - wk->bss->cbss.bssid, 0); + wk->bss->cbss.bssid, NULL, 0, 0); wk->auth_transaction = 2; wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; @@ -1176,7 +1176,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, return; ieee80211_send_auth(sdata, 3, wk->auth_alg, elems.challenge - 2, elems.challenge_len + 2, - wk->bss->cbss.bssid, 1); + wk->bss->cbss.bssid, + wk->key, wk->key_len, wk->key_idx); wk->auth_transaction = 4; } @@ -2175,6 +2176,12 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, wk->ie_len = req->ie_len; } + if (req->key && req->key_len) { + wk->key_len = req->key_len; + wk->key_idx = req->key_idx; + memcpy(wk->key, req->key, req->key_len); + } + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); memcpy(wk->ssid, ssid + 2, ssid[1]); wk->ssid_len = ssid[1]; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 915e777..dbf66b5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -31,6 +31,7 @@ #include "mesh.h" #include "wme.h" #include "led.h" +#include "wep.h" /* privid for wiphys to determine whether they belong to us or not */ void *mac80211_wiphy_privid = &mac80211_wiphy_privid; @@ -804,12 +805,13 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, - u8 *extra, size_t extra_len, - const u8 *bssid, int encrypt) + u8 *extra, size_t extra_len, const u8 *bssid, + const u8 *key, u8 key_len, u8 key_idx) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + int err; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 6 + extra_len); @@ -824,8 +826,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, memset(mgmt, 0, 24 + 6); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); - if (encrypt) - mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); @@ -835,7 +835,13 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); - ieee80211_tx_skb(sdata, skb, encrypt); + if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { + mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); + WARN_ON(err); + } + + ieee80211_tx_skb(sdata, skb, 0); } int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 4fafb2d..8a980f1 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -144,9 +144,9 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ -static int ieee80211_wep_encrypt(struct ieee80211_local *local, - struct sk_buff *skb, - const u8 *key, int keylen, int keyidx) +int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx) { u8 *iv; size_t len; diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index 85219de..fe29d7e 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -20,6 +20,9 @@ int ieee80211_wep_init(struct ieee80211_local *local); void ieee80211_wep_free(struct ieee80211_local *local); void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); +int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx); int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); diff --git a/net/wireless/core.c b/net/wireless/core.c index 1a78b3c..97cc596 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -666,14 +666,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - if (wdev->wext.ibss.ssid_len) - __cfg80211_join_ibss(rdev, dev, - &wdev->wext.ibss); + cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION: - if (wdev->wext.connect.ssid_len) - __cfg80211_connect(rdev, dev, - &wdev->wext.connect); + cfg80211_mgd_wext_connect(rdev, wdev); break; default: break; @@ -690,6 +686,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, } mutex_unlock(&rdev->devlist_mtx); mutex_destroy(&wdev->mtx); +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.keys); +#endif break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) diff --git a/net/wireless/core.h b/net/wireless/core.h index e46cd6e..2ec8ddb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -238,6 +238,12 @@ struct cfg80211_event { }; }; +struct cfg80211_cached_keys { + struct key_params params[6]; + u8 data[6][WLAN_MAX_KEY_LEN]; + int def, defmgmt; +}; + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -256,14 +262,18 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, /* IBSS */ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params); + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, @@ -272,12 +282,14 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len); + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -310,10 +322,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect); + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys); int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); @@ -323,11 +337,14 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); @@ -335,5 +352,6 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); +void cfg80211_upload_connect_keys(struct wireless_dev *wdev); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 99ef936..9394e78 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -39,6 +39,8 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); + cfg80211_upload_connect_keys(wdev); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT @@ -71,7 +73,8 @@ EXPORT_SYMBOL(cfg80211_ibss_joined); int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -81,13 +84,18 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, if (wdev->ssid_len) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) + kfree(wdev->connect_keys); + wdev->connect_keys = connkeys; + #ifdef CONFIG_WIRELESS_EXT wdev->wext.ibss.channel = params->channel; #endif err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); - - if (err) + if (err) { + wdev->connect_keys = NULL; return err; + } memcpy(wdev->ssid, params->ssid, params->ssid_len); wdev->ssid_len = params->ssid_len; @@ -97,13 +105,14 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_ibss_params *params) + struct cfg80211_ibss_params *params, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params); + err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); return err; @@ -112,9 +121,22 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; ASSERT_WDEV_LOCK(wdev); + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); @@ -172,11 +194,14 @@ int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, } #ifdef CONFIG_WIRELESS_EXT -static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { + struct cfg80211_cached_keys *ck = NULL; enum ieee80211_band band; - int i; + int i, err; + + ASSERT_WDEV_LOCK(wdev); if (!wdev->wext.ibss.beacon_interval) wdev->wext.ibss.beacon_interval = 100; @@ -216,8 +241,24 @@ static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return 0; - return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy), - wdev->netdev, &wdev->wext.ibss); + if (wdev->wext.keys) + wdev->wext.keys->def = wdev->wext.default_key; + + wdev->wext.ibss.privacy = wdev->wext.default_key != -1; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_join_ibss(rdev, wdev->netdev, + &wdev->wext.ibss, ck); + if (err) + kfree(ck); + + return err; } int cfg80211_ibss_wext_siwfreq(struct net_device *dev, @@ -265,7 +306,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); @@ -333,7 +378,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); @@ -414,7 +463,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; - return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_lock(wdev); + err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + wdev_unlock(wdev); + + return err; } /* temporary symbol - mark GPL - in the future the handler won't be */ EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 1b2ca1f..8e4ce2f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -328,7 +328,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; @@ -337,6 +338,10 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); + if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + if (!key || !key_len || key_idx < 0 || key_idx > 4) + return -EINVAL; + if (wdev->current_bss && memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; @@ -359,6 +364,9 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + req.key = key; + req.key_len = key_len; + req.key_idx = key_idx; if (!req.bss) return -ENOENT; @@ -396,13 +404,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len) + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50cf593..45c5f9c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -138,8 +138,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { /* policy for the attributes */ static struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { - [NL80211_KEY_DATA] = { .type = NLA_BINARY, - .len = WLAN_MAX_KEY_LEN }, + [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, @@ -305,6 +304,83 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) return 0; } +static struct cfg80211_cached_keys * +nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, + struct nlattr *keys) +{ + struct key_parse parse; + struct nlattr *key; + struct cfg80211_cached_keys *result; + int rem, err, def = 0; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return ERR_PTR(-ENOMEM); + + result->def = -1; + result->defmgmt = -1; + + nla_for_each_nested(key, keys, rem) { + memset(&parse, 0, sizeof(parse)); + parse.idx = -1; + + err = nl80211_parse_key_new(key, &parse); + if (err) + goto error; + err = -EINVAL; + if (!parse.p.key) + goto error; + if (parse.idx < 0 || parse.idx > 4) + goto error; + if (parse.def) { + if (def) + goto error; + def = 1; + result->def = parse.idx; + } else if (parse.defmgmt) + goto error; + err = cfg80211_validate_key_settings(rdev, &parse.p, + parse.idx, NULL); + if (err) + goto error; + result->params[parse.idx].cipher = parse.p.cipher; + result->params[parse.idx].key_len = parse.p.key_len; + result->params[parse.idx].key = result->data[parse.idx]; + memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + } + + return result; + error: + kfree(result); + return ERR_PTR(err); +} + +static int nl80211_key_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + break; + case NL80211_IFTYPE_ADHOC: + if (!wdev->current_bss) + return -ENOLINK; + break; + case NL80211_IFTYPE_STATION: + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOLINK; + break; + default: + return -EINVAL; + } + + return 0; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -1212,7 +1288,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = func(&rdev->wiphy, dev, key.idx); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = func(&rdev->wiphy, dev, key.idx); + #ifdef CONFIG_WIRELESS_EXT if (!err) { if (func == rdev->ops->set_default_key) @@ -1221,6 +1301,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1235,7 +1316,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int err, i; + int err; struct net_device *dev; struct key_parse key; u8 *mac_addr = NULL; @@ -1250,29 +1331,28 @@ 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 (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr)) - return -EINVAL; - rtnl_lock(); err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) - if (key.p.cipher == rdev->wiphy.cipher_suites[i]) - break; - if (i == rdev->wiphy.n_cipher_suites) { - err = -EINVAL; + if (!rdev->ops->add_key) { + err = -EOPNOTSUPP; goto out; } - if (!rdev->ops->add_key) { - err = -EOPNOTSUPP; + if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) { + err = -EINVAL; goto out; } - err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, + mac_addr, &key.p); + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -1309,7 +1389,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) goto out; } - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); + wdev_lock(dev->ieee80211_ptr); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (!err) + err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1319,6 +1402,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->wext.default_mgmt_key = -1; } #endif + wdev_unlock(dev->ieee80211_ptr); out: cfg80211_unlock_rdev(rdev); @@ -3159,6 +3243,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) const u8 *bssid, *ssid, *ie = NULL; int err, ssid_len, ie_len = 0; enum nl80211_auth_type auth_type; + struct key_parse key; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3175,6 +3260,25 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; + err = nl80211_parse_key(info, &key); + if (err) + return err; + + if (key.idx >= 0) { + if (!key.p.key || !key.p.key_len) + return -EINVAL; + if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || + key.p.key_len != WLAN_KEY_LEN_WEP40) && + (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || + key.p.key_len != WLAN_KEY_LEN_WEP104)) + return -EINVAL; + if (key.idx > 4) + return -EINVAL; + } else { + key.p.key_len = 0; + key.p.key = NULL; + } + rtnl_lock(); err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); @@ -3219,7 +3323,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) } err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len); + ssid, ssid_len, ie, ie_len, + key.p.key, key.p.key_len, key.idx); out: cfg80211_unlock_rdev(rdev); @@ -3506,6 +3611,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&ibss, 0, sizeof(ibss)); @@ -3570,13 +3676,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; + ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } - err = cfg80211_join_ibss(rdev, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } @@ -3746,6 +3865,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) struct net_device *dev; struct cfg80211_connect_params connect; struct wiphy *wiphy; + struct cfg80211_cached_keys *connkeys = NULL; int err; memset(&connect, 0, sizeof(connect)); @@ -3810,12 +3930,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_connect(rdev, dev, &connect); + if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { + connkeys = nl80211_parse_connkeys(rdev, + info->attrs[NL80211_ATTR_KEYS]); + if (IS_ERR(connkeys)) { + err = PTR_ERR(connkeys); + connkeys = NULL; + goto out; + } + } + + err = cfg80211_connect(rdev, dev, &connect, connkeys); out: cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: + if (err) + kfree(connkeys); rtnl_unlock(); return err; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 79ca56c..d635a99 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -125,7 +125,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->channel, params->auth_type, params->bssid, params->ssid, params->ssid_len, - NULL, 0); + NULL, 0, + params->key, params->key_len, + params->key_idx); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -279,8 +281,12 @@ void cfg80211_sme_rx_auth(struct net_device *dev, /* select automatically between only open, shared, leap */ switch (wdev->conn->params.auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_SHARED_KEY; + if (wdev->connect_keys) + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + else + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; break; case NL80211_AUTHTYPE_SHARED_KEY: wdev->conn->params.auth_type = @@ -353,10 +359,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, #endif if (status == WLAN_STATUS_SUCCESS && - wdev->sme_state == CFG80211_SME_IDLE) { - wdev->sme_state = CFG80211_SME_CONNECTED; - return; - } + wdev->sme_state == CFG80211_SME_IDLE) + goto success; if (wdev->sme_state != CFG80211_SME_CONNECTING) return; @@ -370,24 +374,29 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; - if (status == WLAN_STATUS_SUCCESS) { - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - - if (WARN_ON(!bss)) - return; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - wdev->sme_state = CFG80211_SME_CONNECTED; - } else { + if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; kfree(wdev->conn); wdev->conn = NULL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + return; } + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + success: + wdev->sme_state = CFG80211_SME_CONNECTED; + cfg80211_upload_connect_keys(wdev); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -516,6 +525,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int i; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -543,8 +554,15 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->conn = NULL; } - nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, - reason, ie, ie_len, from_ap); + nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); + + /* + * Delete all the keys ... pairwise keys can't really + * exist any more anyway, but default keys might. + */ + if (rdev->ops->del_key) + for (i = 0; i < 6; i++) + rdev->ops->del_key(wdev->wiphy, dev, i, NULL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); @@ -580,7 +598,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -590,6 +609,24 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; + if (WARN_ON(wdev->connect_keys)) { + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + } + + if (connkeys && connkeys->def >= 0) { + int idx; + + idx = connkeys->def; + /* If given a WEP key we may need it for shared key auth */ + if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || + connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { + connect->key_idx = idx; + connect->key = connkeys->params[idx].key; + connect->key_len = connkeys->params[idx].key_len; + } + } + if (!rdev->ops->connect) { if (!rdev->ops->auth || !rdev->ops->assoc) return -EOPNOTSUPP; @@ -640,6 +677,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, cfg80211_get_conn_bss(wdev); wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { @@ -662,13 +700,16 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; + wdev->connect_keys = NULL; } return err; } else { wdev->sme_state = CFG80211_SME_CONNECTING; + wdev->connect_keys = connkeys; err = rdev->ops->connect(&rdev->wiphy, dev, connect); if (err) { + wdev->connect_keys = NULL; wdev->sme_state = CFG80211_SME_IDLE; return err; } @@ -682,12 +723,13 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_connect_params *connect) + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys) { int err; wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect); + err = __cfg80211_connect(rdev, dev, connect, connkeys); wdev_unlock(dev->ieee80211_ptr); return err; @@ -704,6 +746,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, if (wdev->sme_state == CFG80211_SME_IDLE) return -EINVAL; + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; + if (!rdev->ops->disconnect) { if (!rdev->ops->deauth) return -EOPNOTSUPP; diff --git a/net/wireless/util.c b/net/wireless/util.c index 28f8f96..4bab380 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -141,9 +141,12 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy) set_mandatory_flags_band(wiphy->bands[band], band); } -int cfg80211_validate_key_settings(struct key_params *params, int key_idx, +int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct key_params *params, int key_idx, const u8 *mac_addr) { + int i; + if (key_idx > 5) return -EINVAL; @@ -197,6 +200,12 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, } } + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params->cipher == rdev->wiphy.cipher_suites[i]) + break; + if (i == rdev->wiphy.n_cipher_suites) + return -EINVAL; + return 0; } @@ -523,3 +532,33 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) return NULL; } EXPORT_SYMBOL(ieee80211_bss_get_ie); + +void cfg80211_upload_connect_keys(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct net_device *dev = wdev->netdev; + int i; + + if (!wdev->connect_keys) + return; + + for (i = 0; i < 6; i++) { + if (!wdev->connect_keys->params[i].cipher) + continue; + if (rdev->ops->add_key(wdev->wiphy, dev, i, NULL, + &wdev->connect_keys->params[i])) + printk(KERN_ERR "%s: failed to set key %d\n", + dev->name, i); + if (wdev->connect_keys->def == i) + if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set defkey %d\n", + dev->name, i); + if (wdev->connect_keys->defmgmt == i) + if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) + printk(KERN_ERR "%s: failed to set mgtdef %d\n", + dev->name, i); + } + + kfree(wdev->connect_keys); + wdev->connect_keys = NULL; +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 5088d89..5d01763 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -453,15 +453,32 @@ int cfg80211_wext_giwretry(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *addr, - bool remove, bool tx_key, int idx, - struct key_params *params) +static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; + int err, i; + + if (!wdev->wext.keys) { + wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), + GFP_KERNEL); + if (!wdev->wext.keys) + return -ENOMEM; + for (i = 0; i < 6; i++) + wdev->wext.keys->params[i].key = + wdev->wext.keys->data[i]; + } + + if (wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (!wdev->current_bss) + return -ENOLINK; + if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP; @@ -471,8 +488,14 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return -EINVAL; if (remove) { - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); + err = 0; + if (wdev->current_bss) + err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr); if (!err) { + if (!addr) { + wdev->wext.keys->params[idx].key_len = 0; + wdev->wext.keys->params[idx].cipher = 0; + } if (idx == wdev->wext.default_key) wdev->wext.default_key = -1; else if (idx == wdev->wext.default_mgmt_key) @@ -486,36 +509,64 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return 0; return err; - } else { - if (addr) - tx_key = false; + } - if (cfg80211_validate_key_settings(params, idx, addr)) - return -EINVAL; + if (addr) + tx_key = false; + if (cfg80211_validate_key_settings(rdev, params, idx, addr)) + return -EINVAL; + + err = 0; + if (wdev->current_bss) err = rdev->ops->add_key(&rdev->wiphy, dev, idx, addr, params); - if (err) - return err; + if (err) + return err; + + if (!addr) { + wdev->wext.keys->params[idx] = *params; + memcpy(wdev->wext.keys->data[idx], + params->key, params->key_len); + wdev->wext.keys->params[idx].key = + wdev->wext.keys->data[idx]; + } - if (tx_key || (!addr && wdev->wext.default_key == -1)) { + if (params->cipher != WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_key = idx; - return err; - } + if (!err) + wdev->wext.default_key = idx; + return err; + } - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && - (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && + (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { + if (wdev->current_bss) err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, dev, idx); - if (!err) - wdev->wext.default_mgmt_key = idx; - return err; - } - - return 0; + if (!err) + wdev->wext.default_mgmt_key = idx; + return err; } + + return 0; +} + +static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + bool remove, bool tx_key, int idx, + struct key_params *params) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_set_encryption(rdev, dev, addr, remove, + tx_key, idx, params); + wdev_unlock(dev->ieee80211_ptr); + + return err; } int cfg80211_wext_siwencode(struct net_device *dev, @@ -528,6 +579,10 @@ int cfg80211_wext_siwencode(struct net_device *dev, bool remove = false; struct key_params params; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -548,9 +603,14 @@ int cfg80211_wext_siwencode(struct net_device *dev, remove = true; else if (erq->length == 0) { /* No key data - just set the default TX key index */ - err = rdev->ops->set_default_key(&rdev->wiphy, dev, idx); + err = 0; + wdev_lock(wdev); + if (wdev->current_bss) + err = rdev->ops->set_default_key(&rdev->wiphy, + dev, idx); if (!err) wdev->wext.default_key = idx; + wdev_unlock(wdev); return err; } @@ -583,6 +643,10 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, struct key_params params; u32 cipher; + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + /* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key || !rdev->ops->add_key || @@ -656,37 +720,15 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_siwencodeext); -struct giwencode_cookie { - size_t buflen; - char *keybuf; -}; - -static void giwencode_get_key_cb(void *cookie, struct key_params *params) -{ - struct giwencode_cookie *data = cookie; - - if (!params->key) { - data->buflen = 0; - return; - } - - data->buflen = min_t(size_t, data->buflen, params->key_len); - memcpy(data->keybuf, params->key, data->buflen); -} - int cfg80211_wext_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int idx, err; - struct giwencode_cookie data = { - .keybuf = keybuf, - .buflen = erq->length, - }; + int idx; - if (!rdev->ops->get_key) + if (wdev->iftype != NL80211_IFTYPE_STATION && + wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP; idx = erq->flags & IW_ENCODE_INDEX; @@ -701,21 +743,18 @@ int cfg80211_wext_giwencode(struct net_device *dev, erq->flags = idx + 1; - err = rdev->ops->get_key(&rdev->wiphy, dev, idx, NULL, &data, - giwencode_get_key_cb); - if (!err) { - erq->length = data.buflen; - erq->flags |= IW_ENCODE_ENABLED; - return 0; - } - - if (err == -ENOENT) { + if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { erq->flags |= IW_ENCODE_DISABLED; erq->length = 0; return 0; } - return err; + erq->length = min_t(size_t, erq->length, + wdev->wext.keys->params[idx].key_len); + memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); + erq->flags |= IW_ENCODE_ENABLED; + + return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 6f75aaa7f..c33ea9a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -10,10 +10,11 @@ #include #include "nl80211.h" -static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { - int err; + struct cfg80211_cached_keys *ck = NULL; + int err, i; ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); @@ -25,10 +26,25 @@ static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, wdev->wext.connect.ie_len = wdev->wext.ie_len; wdev->wext.connect.privacy = wdev->wext.default_key != -1; - err = 0; - if (wdev->wext.connect.ssid_len != 0) - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect); + if (wdev->wext.keys) { + wdev->wext.keys->def = wdev->wext.default_key; + wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; + } + + if (!wdev->wext.connect.ssid_len) + return 0; + + if (wdev->wext.keys) { + ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); + if (!ck) + return -ENOMEM; + for (i = 0; i < 6; i++) + ck->params[i].key = ck->data[i]; + } + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect, ck); + if (err) + kfree(ck); return err; } -- cgit v1.1 From 80e5b06a1b0d9a6aa88dc5c9e93ba49510c52b45 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Thu, 9 Jul 2009 16:59:49 +0800 Subject: cfg80211: fix NULL dereference in IBSS SIOCGIWAP This patch avoids memcpy from wdev->wext.ibss.bssid if it is NULL. This could happen if we SIOCGIWAP before SIOCSIWAP. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/wireless/ibss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 9394e78..8b65e21 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -487,8 +487,11 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, wdev_lock(wdev); if (wdev->current_bss) memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); - else + else if (wdev->wext.ibss.bssid) memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + wdev_unlock(wdev); return 0; -- cgit v1.1 From ca386f3137eb68621fadba546d9eb35ac2f82de3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 02:39:48 +0200 Subject: mac80211: fix multi-use timer We have, sometimes, multiple things that want to run but don't have their own timer. Introduce a new function to mac80211's mlme run_again() that makes sure that the timer will run again at the _first_ needed time, use that function and also properly reprogram the timer once it fired. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8e4a604..c1114bb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -72,6 +72,26 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) WARN_ON(!mutex_is_locked(&ifmgd->mtx)); } +/* + * We can have multiple work items (and connection probing) + * scheduling this timer, but we need to take care to only + * reschedule it when it should fire _earlier_ than it was + * asked for before, or if it's not pending right now. This + * function ensures that. Note that it then is required to + * run this function for all timeouts after the first one + * has happened -- the work that runs from this timer will + * do that. + */ +static void run_again(struct ieee80211_if_managed *ifmgd, + unsigned long timeout) +{ + ASSERT_MGD_MTX(ifmgd); + + if (!timer_pending(&ifmgd->timer) || + time_before(timeout, ifmgd->timer.expires)) + mod_timer(&ifmgd->timer, timeout); +} + static int ecw2cw(int ecw) { return (1 << ecw) - 1; @@ -916,7 +936,7 @@ ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0); wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - mod_timer(&ifmgd->timer, wk->timeout); + run_again(ifmgd, wk->timeout); return RX_MGMT_NONE; } @@ -958,7 +978,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, wk->auth_transaction = 2; wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - mod_timer(&ifmgd->timer, wk->timeout); + run_again(ifmgd, wk->timeout); return RX_MGMT_NONE; } @@ -1079,7 +1099,7 @@ ieee80211_associate(struct ieee80211_sub_if_data *sdata, ieee80211_send_assoc(sdata, wk); wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; - mod_timer(&ifmgd->timer, wk->timeout); + run_again(ifmgd, wk->timeout); return RX_MGMT_NONE; } @@ -1140,7 +1160,7 @@ void ieee80211_beacon_loss_work(struct work_struct *work) ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, ssid + 2, ssid[1], NULL, 0); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT); + run_again(ifmgd, jiffies + IEEE80211_PROBE_WAIT); out: mutex_unlock(&ifmgd->mtx); } @@ -1350,8 +1370,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, sdata->dev->name, tu, ms); wk->timeout = jiffies + msecs_to_jiffies(ms); if (ms > IEEE80211_ASSOC_TIMEOUT) - mod_timer(&ifmgd->timer, - jiffies + msecs_to_jiffies(ms)); + run_again(ifmgd, jiffies + msecs_to_jiffies(ms)); return RX_MGMT_NONE; } @@ -1981,8 +2000,15 @@ static void ieee80211_sta_work(struct work_struct *work) } list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { - if (time_before(jiffies, wk->timeout)) + if (time_is_after_jiffies(wk->timeout)) { + /* + * This work item isn't supposed to be worked on + * right now, but take care to adjust the timer + * properly. + */ + run_again(ifmgd, wk->timeout); continue; + } switch (wk->state) { default: -- cgit v1.1 From b291ba11181d46dfbd2d7a5c00a5f3335228191e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 15:29:03 +0200 Subject: mac80211: monitor the connection With the recent MLME rework I accidentally removed the connection monitoring code. In order to add it back, this patch will add new code to monitor both for beacon loss and for the connection actually working, with possibly separate triggers. When no unicast frames have been received from the AP for (currently) two seconds, we will send the AP a probe request. Also, when we don't see beacons from the AP for two seconds, we do the same (but those times need not be the same due to the way the code is now written). Additionally, clean up the parameters to the ieee80211_set_disassoc() function that I need here, those are all useless except sdata. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 20 ++-- net/mac80211/mlme.c | 257 ++++++++++++++++++++++++++++++++++++--------- net/mac80211/rx.c | 22 ++-- 3 files changed, 227 insertions(+), 72 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 06b3411..a34bca2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -256,12 +256,13 @@ struct ieee80211_mgd_work { /* flags used in struct ieee80211_if_managed.flags */ enum ieee80211_sta_flags { - IEEE80211_STA_PROBEREQ_POLL = BIT(3), - IEEE80211_STA_CONTROL_PORT = BIT(4), - IEEE80211_STA_WMM_ENABLED = BIT(5), - IEEE80211_STA_DISABLE_11N = BIT(6), - IEEE80211_STA_CSA_RECEIVED = BIT(7), - IEEE80211_STA_MFP_ENABLED = BIT(8), + IEEE80211_STA_BEACON_POLL = BIT(0), + IEEE80211_STA_CONNECTION_POLL = BIT(1), + IEEE80211_STA_CONTROL_PORT = BIT(2), + IEEE80211_STA_WMM_ENABLED = BIT(3), + IEEE80211_STA_DISABLE_11N = BIT(4), + IEEE80211_STA_CSA_RECEIVED = BIT(5), + IEEE80211_STA_MFP_ENABLED = BIT(6), }; /* flags for MLME request */ @@ -271,11 +272,16 @@ enum ieee80211_sta_request { struct ieee80211_if_managed { struct timer_list timer; + struct timer_list conn_mon_timer; + struct timer_list bcn_mon_timer; struct timer_list chswitch_timer; struct work_struct work; + struct work_struct monitor_work; struct work_struct chswitch_work; struct work_struct beacon_loss_work; + unsigned long probe_timeout; + struct mutex mtx; struct ieee80211_bss *associated; struct list_head work_list; @@ -292,8 +298,6 @@ struct ieee80211_if_managed { unsigned long request; - unsigned long last_beacon; - unsigned int flags; u32 beacon_crc; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c1114bb..18dad22 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -31,8 +31,23 @@ #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 -#define IEEE80211_MONITORING_INTERVAL (2 * HZ) -#define IEEE80211_PROBE_WAIT (HZ / 5) + +/* + * beacon loss detection timeout + * XXX: should depend on beacon interval + */ +#define IEEE80211_BEACON_LOSS_TIME (2 * HZ) +/* + * Time the connection can be idle before we probe + * it to see if we can still talk to the AP. + */ +#define IEEE80211_CONNECTION_IDLE_TIME (2 * HZ) +/* + * Time we wait for a probe response after sending + * a probe request because of beacon loss or for + * checking the connection still works. + */ +#define IEEE80211_PROBE_WAIT (HZ / 5) #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 @@ -92,6 +107,15 @@ static void run_again(struct ieee80211_if_managed *ifmgd, mod_timer(&ifmgd->timer, timeout); } +static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER) + return; + + mod_timer(&sdata->u.mgd.bcn_mon_timer, + round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME)); +} + static int ecw2cw(int ecw) { return (1 << ecw) - 1; @@ -666,7 +690,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) if (count == 1 && found->u.mgd.powersave && found->u.mgd.associated && list_empty(&found->u.mgd.work_list) && - !(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) { + !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL))) { s32 beaconint_us; if (latency < 0) @@ -872,6 +897,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.associated = bss; memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN); + /* just to be sure */ + sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | + IEEE80211_STA_BEACON_POLL); + ieee80211_led_assoc(local, 1); sdata->vif.bss_conf.assoc = 1; @@ -983,16 +1012,21 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; } -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, - const u8 *bssid, bool deauth) +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 sta_info *sta; u32 changed = 0, config_changed = 0; + u8 bssid[ETH_ALEN]; ASSERT_MGD_MTX(ifmgd); + if (WARN_ON(!ifmgd->associated)) + return; + + memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN); + ifmgd->associated = NULL; memset(ifmgd->bssid, 0, ETH_ALEN); @@ -1112,32 +1146,22 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, * from AP because we know that the connection is working both ways * at that time. But multicast frames (and hence also beacons) must * be ignored here, because we need to trigger the timer during - * data idle periods for sending the periodical probe request to - * the AP. + * data idle periods for sending the periodic probe request to the + * AP we're connected to. */ - if (!is_multicast_ether_addr(hdr->addr1)) - mod_timer(&sdata->u.mgd.timer, - jiffies + IEEE80211_MONITORING_INTERVAL); + if (is_multicast_ether_addr(hdr->addr1)) + return; + + mod_timer(&sdata->u.mgd.conn_mon_timer, + round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } -void ieee80211_beacon_loss_work(struct work_struct *work) +static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, + bool beacon) { - 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; const u8 *ssid; - - /* - * The driver has already reported this event and we have - * already sent a probe request. Maybe the AP died and the - * driver keeps reporting until we disassociate... We have - * to ignore that because otherwise we would continually - * reset the timer and never check whether we received a - * probe response! - */ - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) - return; + bool already = false; mutex_lock(&ifmgd->mtx); @@ -1145,12 +1169,35 @@ void ieee80211_beacon_loss_work(struct work_struct *work) goto out; #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) - printk(KERN_DEBUG "%s: driver reports beacon loss from AP " + if (beacon && net_ratelimit()) + printk(KERN_DEBUG "%s: detected beacon loss from AP " "- sending probe request\n", sdata->dev->name); #endif - ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + /* + * The driver/our work has already reported this event or the + * connection monitoring has kicked in and we have already sent + * a probe request. Or maybe the AP died and the driver keeps + * reporting until we disassociate... + * + * In either case we have to ignore the current call to this + * function (except for setting the correct probe reason bit) + * because otherwise we would reset the timer every time and + * never check whether we received a probe response! + */ + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) + already = true; + + if (beacon) + ifmgd->flags |= IEEE80211_STA_BEACON_POLL; + else + ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; + + if (already) + goto out; + + ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT; mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); @@ -1160,11 +1207,21 @@ void ieee80211_beacon_loss_work(struct work_struct *work) ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, ssid + 2, ssid[1], NULL, 0); - run_again(ifmgd, jiffies + IEEE80211_PROBE_WAIT); + run_again(ifmgd, ifmgd->probe_timeout); + out: mutex_unlock(&ifmgd->mtx); } +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); + + ieee80211_mgd_probe_ap(sdata, true); +} + void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -1278,7 +1335,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, sdata->dev->name, bssid, reason_code); if (!wk) { - ieee80211_set_disassoc(sdata, bssid, true); + ieee80211_set_disassoc(sdata); } else { list_del(&wk->list); kfree(wk); @@ -1311,7 +1368,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - ieee80211_set_disassoc(sdata, ifmgd->associated->cbss.bssid, false); + ieee80211_set_disassoc(sdata); return RX_MGMT_CFG80211_DISASSOC; } @@ -1412,9 +1469,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; } - /* update new sta with its last rx activity */ - sta->last_rx = jiffies; - set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP); if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) @@ -1517,10 +1571,11 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_set_associated(sdata, wk->bss, changed); /* - * initialise the time of last beacon to be the association time, - * otherwise beacon loss check will trigger immediately + * Start timer to probe the connection to the AP now. + * Also start the timer that will detect beacon loss. */ - ifmgd->last_beacon = jiffies; + ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); + mod_beacon_timer(sdata); list_del(&wk->list); kfree(wk); @@ -1604,11 +1659,22 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 && - ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) { + ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | + IEEE80211_STA_BEACON_POLL); mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); mutex_unlock(&sdata->local->iflist_mtx); + /* + * We've received a probe response, but are not sure whether + * we have or will be receiving any beacons or data, so let's + * schedule the timers again, just in case. + */ + mod_beacon_timer(sdata); + mod_timer(&ifmgd->conn_mon_timer, + round_jiffies_up(jiffies + + IEEE80211_CONNECTION_IDLE_TIME)); } } @@ -1658,27 +1724,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (rx_status->freq != local->hw.conf.channel->center_freq) return; - if (WARN_ON(!ifmgd->associated)) + /* + * We might have received a number of frames, among them a + * disassoc frame and a beacon... + */ + if (!ifmgd->associated) return; bssid = ifmgd->associated->cbss.bssid; - if (WARN_ON(memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)) + /* + * And in theory even frames from a different AP we were just + * associated to a split-second ago! + */ + if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) return; - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { + if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) { printk(KERN_DEBUG "%s: cancelling probereq poll due " "to a received beacon\n", sdata->dev->name); } #endif - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); } + /* + * Push the beacon loss detection into the future since + * we are processing a beacon from the AP just now. + */ + mod_beacon_timer(sdata); + ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, len - baselen, &elems, @@ -1980,6 +2060,37 @@ static void ieee80211_sta_work(struct work_struct *work) /* then process the rest of the work */ mutex_lock(&ifmgd->mtx); + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL) && + ifmgd->associated) { + if (time_is_after_jiffies(ifmgd->probe_timeout)) + run_again(ifmgd, ifmgd->probe_timeout); + else { + u8 bssid[ETH_ALEN]; + /* + * We actually lost the connection ... or did we? + * Let's make sure! + */ + ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | + IEEE80211_STA_BEACON_POLL); + memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN); + printk(KERN_DEBUG "No probe response from AP %pM" + " after %dms, disconnecting.\n", + bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); + ieee80211_set_disassoc(sdata); + mutex_unlock(&ifmgd->mtx); + /* + * must be outside lock due to cfg80211, + * but that's not a problem. + */ + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + NULL); + mutex_lock(&ifmgd->mtx); + } + } + list_for_each_entry(wk, &ifmgd->work_list, list) { if (wk->state != IEEE80211_MGD_STATE_IDLE) { anybusy = true; @@ -2067,15 +2178,51 @@ static void ieee80211_sta_work(struct work_struct *work) ieee80211_recalc_idle(local); } +static void ieee80211_sta_bcn_mon_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = + (struct ieee80211_sub_if_data *) data; + struct ieee80211_local *local = sdata->local; + + if (local->quiescing) + return; + + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.beacon_loss_work); +} + +static void ieee80211_sta_conn_mon_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = + (struct ieee80211_sub_if_data *) data; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; + + if (local->quiescing) + return; + + queue_work(local->hw.workqueue, &ifmgd->monitor_work); +} + +static void ieee80211_sta_monitor_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.monitor_work); + + ieee80211_mgd_probe_ap(sdata, false); +} + static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) { if (sdata->vif.type == NL80211_IFTYPE_STATION) { - /* - * Need to update last_beacon to avoid beacon loss - * test to trigger. - */ - sdata->u.mgd.last_beacon = jiffies; + sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL); + /* let's probe the connection once */ + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.monitor_work); + /* and do all the other regular work too */ queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); } @@ -2100,6 +2247,11 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) cancel_work_sync(&ifmgd->chswitch_work); if (del_timer_sync(&ifmgd->chswitch_timer)) set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); + + cancel_work_sync(&ifmgd->monitor_work); + /* these will just be re-established on connection */ + del_timer_sync(&ifmgd->conn_mon_timer); + del_timer_sync(&ifmgd->bcn_mon_timer); } void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) @@ -2120,10 +2272,15 @@ 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->monitor_work, ieee80211_sta_monitor_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->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, + (unsigned long) sdata); + setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, + (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, (unsigned long) sdata); skb_queue_head_init(&ifmgd->skb_queue); @@ -2323,7 +2480,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { bssid = req->bss->bssid; - ieee80211_set_disassoc(sdata, bssid, true); + ieee80211_set_disassoc(sdata); } else list_for_each_entry(wk, &ifmgd->work_list, list) { if (&wk->bss->cbss == req->bss) { bssid = req->bss->bssid; @@ -2365,7 +2522,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return -ENOLINK; } - ieee80211_set_disassoc(sdata, req->bss->bssid, false); + ieee80211_set_disassoc(sdata); mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe6b990..b513fb7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -833,28 +833,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (!sta) return RX_CONTINUE; - /* Update last_rx only for IBSS packets which are for the current - * BSSID to avoid keeping the current IBSS network alive in cases where - * other STAs are using different BSSID. */ + /* + * Update last_rx only for IBSS packets which are for the current + * BSSID to avoid keeping the current IBSS network alive in cases + * where other STAs start using different BSSID. + */ if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, NL80211_IFTYPE_ADHOC); if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) sta->last_rx = jiffies; - } else - if (!is_multicast_ether_addr(hdr->addr1) || - rx->sdata->vif.type == NL80211_IFTYPE_STATION) { - /* Update last_rx only for unicast frames in order to prevent - * the Probe Request frames (the only broadcast frames from a - * STA in infrastructure mode) from keeping a connection alive. + } else if (!is_multicast_ether_addr(hdr->addr1)) { + /* * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ - if (rx->sdata->vif.type == NL80211_IFTYPE_STATION && - ieee80211_is_beacon(hdr->frame_control)) { - rx->sdata->u.mgd.last_beacon = jiffies; - } else - sta->last_rx = jiffies; + sta->last_rx = jiffies; } if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.1 From ec3f149017ef3fd21343b1dcec3589eec6ba5dd5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 02:45:38 +0200 Subject: cfg80211: fix a locking bug The cfg80211_sme_disassoc() function is already holding a lock here that cfg80211_mlme_deauth() would take, so it needs to use __cfg80211_mlme_deauth() instead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d635a99..10ed366 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -826,8 +826,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx) return; memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); - if (cfg80211_mlme_deauth(rdev, dev, bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { + if (__cfg80211_mlme_deauth(rdev, dev, bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { /* whatever -- assume gone anyway */ cfg80211_unhold_bss(wdev->auth_bsses[idx]); cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); -- cgit v1.1 From a43816df2a1a61effcb701037bdf63621d066182 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 11:39:26 +0200 Subject: mac80211: mesh: fix two small problems 1) there's a spin_lock() that needs to be spin_lock_bh() 2) action frames of size 24 might cause an out-of-bounds memory access (for the 25th byte only, so no big deal) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 5 ++++- net/mac80211/mesh_hwmp.c | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 542ea02..8a97b14 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -685,9 +685,12 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) fc = le16_to_cpu(mgmt->frame_control); switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_ACTION: + if (skb->len < IEEE80211_MIN_ACTION_SIZE) + return RX_DROP_MONITOR; + /* fall through */ case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: - case IEEE80211_STYPE_ACTION: skb_queue_tail(&ifmsh->skb_queue, skb); queue_work(local->hw.workqueue, &ifmsh->work); return RX_QUEUED; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index f49ef28..8e86e91 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -686,11 +686,11 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) u8 ttl, dst_flags; u32 lifetime; - spin_lock(&ifmsh->mesh_preq_queue_lock); + spin_lock_bh(&ifmsh->mesh_preq_queue_lock); if (!ifmsh->preq_queue_len || time_before(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) { - spin_unlock(&ifmsh->mesh_preq_queue_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); return; } @@ -698,7 +698,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) struct mesh_preq_queue, list); list_del(&preq_node->list); --ifmsh->preq_queue_len; - spin_unlock(&ifmsh->mesh_preq_queue_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); rcu_read_lock(); mpath = mesh_path_lookup(preq_node->dst, sdata); -- cgit v1.1 From c56c5714f12808e3f702817e72a78dd12f1704eb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 16:54:07 +0200 Subject: cfg80211: fix wext stats Instead of using the wext BSSID which may be NULL if you haven't explicitly set one, we should instead use the current_bss pointer -- if that's NULL we aren't connected anyway. Fixes missing signal quality output reported to me internally at Intel. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 5d01763..aa80c0c 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1209,7 +1209,7 @@ struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) /* we are under RTNL - globally locked - so can use static structs */ static struct iw_statistics wstats; static struct station_info sinfo; - u8 *addr; + u8 bssid[ETH_ALEN]; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) return NULL; @@ -1217,11 +1217,16 @@ struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) if (!rdev->ops->get_station) return NULL; - addr = wdev->wext.connect.bssid; - if (!addr) + /* Grab BSSID of current BSS, if any */ + wdev_lock(wdev); + if (!wdev->current_bss) { + wdev_unlock(wdev); return NULL; + } + memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); + wdev_unlock(wdev); - if (rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo)) + if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) return NULL; memset(&wstats, 0, sizeof(wstats)); -- cgit v1.1 From 908d4369a394e816767d566d9c3d15a5af8c1c55 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 17:53:34 +0200 Subject: cfg80211: don't look at wdev->ssid for giwessid This variable is only used internally, _while_ connected. If we use it, the sequence # iwconfig wlan1 essid foo # iwconfig wlan1 essid "" # iwconfig will still display "foo" as the SSID afterwards, which is obviously quite bogus. Fix this by only displaying the wext SSID, if present. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-sme.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index c33ea9a..cd5764f 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -209,11 +209,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->flags = 0; wdev_lock(wdev); - if (wdev->ssid_len) { - data->flags = 1; - data->length = wdev->ssid_len; - memcpy(ssid, wdev->ssid, data->length); - } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { data->flags = 1; data->length = wdev->wext.connect.ssid_len; memcpy(ssid, wdev->wext.connect.ssid, data->length); -- cgit v1.1 From 4697fe4f78df14d37cffa7e8d27cbb02a351c139 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 18:35:49 +0200 Subject: cfg80211: fix wext setting SSID Pavel reported that you can't set the SSID from "foo" to "bar". I tried reproducing, but used different values, with different lengths, and thus never saw the obvious problem. Reported-by: Pavel Roskin Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-sme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index cd5764f..82e913a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -166,7 +166,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (wdev->wext.connect.ssid && len && len == wdev->wext.connect.ssid_len && - memcmp(wdev->wext.connect.ssid, ssid, len)) + memcmp(wdev->wext.connect.ssid, ssid, len) == 0) goto out; if (wdev->sme_state != CFG80211_SME_IDLE) { -- cgit v1.1 From 48ab905d1a81b7df33a33def04a890e4e0c51460 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jul 2009 18:42:31 +0200 Subject: nl80211: report BSS status When connected to a BSS, or joined to an IBSS, we'll want to know in userspace without using wireless extensions, so report the BSS status in the BSS list. Userspace can query the BSS list, display all the information and retrieve the station information as well. For example (from hwsim): $ iw dev wlan1 scan dump BSS 02:00:00:00:00:00 (on wlan1) -- associated freq: 2462 beacon interval: 100 capability: ESS ShortSlotTime (0x0401) signal: -50.00 dBm SSID: j Supported rates: 1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 DS Paramater set: channel 11 ERP: Extended supported rates: 24.0 36.0 48.0 54.0 Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 65 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 45c5f9c..da450ef 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3094,11 +3094,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) 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) + struct wireless_dev *wdev, + struct cfg80211_internal_bss *intbss) { + struct cfg80211_bss *res = &intbss->pub; void *hdr; struct nlattr *bss; + int i; + + ASSERT_WDEV_LOCK(wdev); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); @@ -3107,7 +3111,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, rdev->bss_generation); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); bss = nla_nest_start(msg, NL80211_ATTR_BSS); if (!bss) @@ -3136,6 +3140,28 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, break; } + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + if (intbss == wdev->current_bss) + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_ASSOCIATED); + else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (intbss != wdev->auth_bsses[i]) + continue; + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_AUTHENTICATED); + break; + } + break; + case NL80211_IFTYPE_ADHOC: + if (intbss == wdev->current_bss) + NLA_PUT_U32(msg, NL80211_BSS_STATUS, + NL80211_BSS_STATUS_IBSS_JOINED); + break; + default: + break; + } + nla_nest_end(msg, bss); return genlmsg_end(msg, hdr); @@ -3148,9 +3174,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) { - struct cfg80211_registered_device *dev; - struct net_device *netdev; + struct cfg80211_registered_device *rdev; + struct net_device *dev; struct cfg80211_internal_bss *scan; + struct wireless_dev *wdev; int ifidx = cb->args[0]; int start = cb->args[1], idx = 0; int err; @@ -3171,39 +3198,43 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[0] = ifidx; } - netdev = dev_get_by_index(&init_net, ifidx); - if (!netdev) + dev = dev_get_by_index(&init_net, ifidx); + if (!dev) return -ENODEV; - dev = cfg80211_get_dev_from_ifindex(ifidx); - if (IS_ERR(dev)) { - err = PTR_ERR(dev); + rdev = cfg80211_get_dev_from_ifindex(ifidx); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); goto out_put_netdev; } - spin_lock_bh(&dev->bss_lock); - cfg80211_bss_expire(dev); + wdev = dev->ieee80211_ptr; - list_for_each_entry(scan, &dev->bss_list, list) { + wdev_lock(wdev); + spin_lock_bh(&rdev->bss_lock); + cfg80211_bss_expire(rdev); + + list_for_each_entry(scan, &rdev->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) { + rdev, wdev, scan) < 0) { idx--; goto out; } } out: - spin_unlock_bh(&dev->bss_lock); + spin_unlock_bh(&rdev->bss_lock); + wdev_unlock(wdev); cb->args[1] = idx; err = skb->len; - cfg80211_unlock_rdev(dev); + cfg80211_unlock_rdev(rdev); out_put_netdev: - dev_put(netdev); + dev_put(dev); return err; } -- cgit v1.1 From 596a07c18b35c9df2fb212856241ae0dfe3162b9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 11 Jul 2009 00:17:32 +0200 Subject: cfg80211: fix more bugs in mlme handling The "what-was-I-thinking-if-anything" patch. Clearly, if cfg80211_send_disassoc() does wdev_lock() and then calls __cfg80211_send_disassoc(), the latter shouldn't lock again. And the sme_state test is ... no further comments. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8e4ce2f..5b9b221 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -178,12 +178,12 @@ static void __cfg80211_send_disassoc(struct net_device *dev, bool from_ap; bool done = false; - wdev_lock(wdev); + ASSERT_WDEV_LOCK(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); - if (!wdev->sme_state == CFG80211_SME_CONNECTED) - goto out; + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return; if (wdev->current_bss && memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { @@ -205,8 +205,6 @@ static void __cfg80211_send_disassoc(struct net_device *dev, from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); - out: - wdev_unlock(wdev); } void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, -- cgit v1.1 From 323d566eae1ace41bc674863b58fcc474501a2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Stefanik?= Date: Sun, 12 Jul 2009 02:03:48 +0200 Subject: cfg80211: fix disabling WPA via wext (SIOCSIWAUTH) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cfg80211_set_wpa_version completely missed the use case when disabling WPA, considering IW_AUTH_WPA_VERSION_DISABLED an invalid argument. This caused weird error messages in wpa_supplicant. Signed-off-by: Gábor Stefanik Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index aa80c0c..e6731bf 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -880,9 +880,19 @@ static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) wdev->wext.connect.crypto.wpa_versions = 0; if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | - IW_AUTH_WPA_VERSION_WPA2)) + IW_AUTH_WPA_VERSION_WPA2| + IW_AUTH_WPA_VERSION_DISABLED)) return -EINVAL; + if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && + (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| + IW_AUTH_WPA_VERSION_WPA2))) + return -EINVAL; + + if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) + wdev->wext.connect.crypto.wpa_versions &= + ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) wdev->wext.connect.crypto.wpa_versions |= NL80211_WPA_VERSION_1; -- cgit v1.1 From 0e2b6286805c419d28a4c1e19e3a121af7449b20 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Jul 2009 13:23:39 +0200 Subject: mac80211: cancel the connection monitor timers/work In "mac80211: monitor the connection" I forgot to add code to cancel the new timers & work when the interface is brought down, which isn't a problem if you just bring it down, but _is_ a problem when you destroy the interface. Correct this lapse. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 8 +++++--- net/mac80211/mlme.c | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4839a2d..090aa5a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -449,16 +449,18 @@ static int ieee80211_stop(struct net_device *dev) case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); del_timer_sync(&sdata->u.mgd.timer); + del_timer_sync(&sdata->u.mgd.conn_mon_timer); + del_timer_sync(&sdata->u.mgd.bcn_mon_timer); /* - * If the timer fired while we waited for it, it will have - * requeued the work. Now the work will be running again + * If any of the timers fired while we waited for it, it will + * have queued its work. Now the work will be running again * but will not rearm the timer again because it checks * whether the interface is running, which, at this point, * it no longer is. */ cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); - + cancel_work_sync(&sdata->u.mgd.monitor_work); cancel_work_sync(&sdata->u.mgd.beacon_loss_work); /* diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 18dad22..e3b3156 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1163,6 +1163,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, const u8 *ssid; bool already = false; + if (!netif_running(sdata->dev)) + return; + mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) -- cgit v1.1 From 6682588a08b8be34649348051bc0204f7ab401a2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Jul 2009 13:24:44 +0200 Subject: cfg80211: fix unregistration The work that we cancel there requires the cfg80211_mutex, so we can't cancel it under the mutex, which is fine, we can just move it to after the locked section. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 97cc596..6891cd0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -548,11 +548,6 @@ void wiphy_unregister(struct wiphy *wiphy) /* unlock again before freeing */ mutex_unlock(&rdev->mtx); - cancel_work_sync(&rdev->conn_work); - cancel_work_sync(&rdev->scan_done_wk); - kfree(rdev->scan_req); - flush_work(&rdev->event_work); - cfg80211_debugfs_rdev_del(rdev); /* If this device got a regulatory hint tell core its @@ -564,6 +559,11 @@ void wiphy_unregister(struct wiphy *wiphy) debugfs_remove(rdev->wiphy.debugfsdir); mutex_unlock(&cfg80211_mutex); + + cancel_work_sync(&rdev->conn_work); + cancel_work_sync(&rdev->scan_done_wk); + kfree(rdev->scan_req); + flush_work(&rdev->event_work); } EXPORT_SYMBOL(wiphy_unregister); -- cgit v1.1 From b770b43e95a66587fbd8c1841de83da87fbf23ea Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 16 Jul 2009 10:15:09 -0700 Subject: mac80211: drop frames for sta with no valid rate When we're associated we should be able to send data to target sta. If we cannot we may be trying to use the incorrect band to talk to the sta. Lets catch any such cases, warn, and drop the frames to not invalidate assumptions being made on rate control algorithms when they have a valid sta to communicate with. Any such cases should be handled and fixed. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 60ae086..f3efd4f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -512,6 +512,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) int i, len; bool inval = false, rts = false, short_preamble = false; struct ieee80211_tx_rate_control txrc; + u32 sta_flags; memset(&txrc, 0, sizeof(txrc)); @@ -544,7 +545,26 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) txrc.short_preamble = short_preamble = true; + sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; + + /* + * Lets not bother rate control if we're associated and cannot + * talk to the sta. This should not happen. + */ + if (WARN((tx->local->sw_scanning) && + (sta_flags & WLAN_STA_ASSOC) && + !rate_usable_index_exists(sband, &tx->sta->sta), + "%s: Dropped data frame as no usable bitrate found while " + "scanning and associated. Target station: " + "%pM on %d GHz band\n", + tx->dev->name, hdr->addr1, + tx->channel->band ? 5 : 2)) + return TX_DROP; + /* + * If we're associated with the sta at this point we know we can at + * least send the frame at the lowest bit rate. + */ rate_control_get_rate(tx->sdata, tx->sta, &txrc); if (unlikely(info->control.rates[0].idx < 0)) -- cgit v1.1 From e8986436580caf50ebbd3bf8371074aadf95aba5 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 14 Jul 2009 20:14:06 -0400 Subject: mac80211: make minstrel/pid RC use ieee80211_is_data(fc) Cc: Felix Fietkau Cc: Derek Smithies Cc: Chittajit Mitra Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel.c | 7 +++---- net/mac80211/rc80211_pid_algo.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 37771ab..5bdce0c 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -75,12 +75,11 @@ use_low_rate(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u16 fc; + __le16 fc; - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; - return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || - (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA); + return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc)); } diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index a0bef76..5496077 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -280,7 +280,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rc_pid_sta_info *spinfo = priv_sta; int rateidx; - u16 fc; + __le16 fc; if (txrc->rts) info->control.rates[0].count = @@ -290,9 +290,8 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, txrc->hw->conf.short_frame_max_tx_count; /* Send management frames and NO_ACK data using lowest rate. */ - fc = le16_to_cpu(hdr->frame_control); - if (!sta || !spinfo || - (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || + fc = hdr->frame_control; + if (!sta || !spinfo || !ieee80211_is_data(fc) || info->flags & IEEE80211_TX_CTL_NO_ACK) { info->control.rates[0].idx = rate_lowest_index(sband, sta); if (info->flags & IEEE80211_TX_CTL_NO_ACK) -- cgit v1.1 From 4c6d4f5c33fbe19b134c1af43af166fee79eb986 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 16 Jul 2009 10:05:41 -0700 Subject: mac80211: add helper for management / no-ack frame rate decision All current rate control algorithms agree to send management and no-ack frames at the lowest rate. They also agree to do this when sta and the private rate control data is NULL. We add a hlper to mac80211 for this and simplify the rate control algorithm code. Developers wishing to make enhancements to rate control algorithms are for broadcast/multicast can opt to not use this in their gate_rate() mac80211 callback. Cc: Zhu Yi Acked-by: Reinette Chatre Cc: ipw3945-devel@lists.sourceforge.net Cc: Gabor Juhos Acked-by: Felix Fietkau Cc: Derek Smithies Cc: Chittajit Mitra Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/rate.c | 29 +++++++++++++++++++++++++++++ net/mac80211/rc80211_minstrel.c | 22 +--------------------- net/mac80211/rc80211_pid_algo.c | 11 +---------- 3 files changed, 31 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 4641f00..8ac7a98 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -198,6 +198,35 @@ static void rate_control_release(struct kref *kref) kfree(ctrl_ref); } +static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) +{ + struct sk_buff *skb = txrc->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + __le16 fc; + + fc = hdr->frame_control; + + return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc)); +} + +bool rate_control_send_low(struct ieee80211_sta *sta, + void *priv_sta, + struct ieee80211_tx_rate_control *txrc) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); + + if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { + info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); + info->control.rates[0].count = + (info->flags & IEEE80211_TX_CTL_NO_ACK) ? + 1 : txrc->hw->max_rate_tries; + return true; + } + return false; +} +EXPORT_SYMBOL(rate_control_send_low); + void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc) diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 5bdce0c..7c51429 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -70,19 +70,6 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix) return i; } -static inline bool -use_low_rate(struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - __le16 fc; - - fc = hdr->frame_control; - - return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc)); -} - - static void minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) { @@ -231,7 +218,6 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; - struct ieee80211_supported_band *sband = txrc->sband; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct minstrel_sta_info *mi = priv_sta; struct minstrel_priv *mp = priv; @@ -244,14 +230,8 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, int mrr_ndx[3]; int sample_rate; - if (!sta || !mi || use_low_rate(skb)) { - ar[0].idx = rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - ar[0].count = 1; - else - ar[0].count = mp->max_retry; + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot; diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 5496077..8c053be 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -276,11 +276,9 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, { struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rc_pid_sta_info *spinfo = priv_sta; int rateidx; - __le16 fc; if (txrc->rts) info->control.rates[0].count = @@ -290,15 +288,8 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, txrc->hw->conf.short_frame_max_tx_count; /* Send management frames and NO_ACK data using lowest rate. */ - fc = hdr->frame_control; - if (!sta || !spinfo || !ieee80211_is_data(fc) || - info->flags & IEEE80211_TX_CTL_NO_ACK) { - info->control.rates[0].idx = rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->control.rates[0].count = 1; - + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } rateidx = spinfo->txrate_idx; -- cgit v1.1 From ae9e4b0d1a43fd66da43918491834f9e5c1b6cca Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 14 Jul 2009 20:23:15 -0400 Subject: cfg80211: treat ieee80211_regdom hints as user hints We were treating ieee80211_regdom module parameter hints as core hints, this means we were not letting the user help compliance further when using the module parameter. It also meant that users with a device with a custom regulatory domain set (wiphy->custom_regulatory) using this module parameter were being stuck to the original default core static regualtory domain. We fix this by using the static cfg80211_regdomain alpha2 as the core hint and treating the module parameter separately. All iwlwifi and ath5k/ath9k/ar9170 devices which world roam set the wiphy->custom_regulatory. This change allows users using this module parameter to have it trated as a a proper user hint and not have it ignored. Signed-off-by: Luis R. Rodriguez Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/reg.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2b4a6c6..fb40428 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -113,11 +113,7 @@ static const struct ieee80211_regdomain world_regdom = { static const struct ieee80211_regdomain *cfg80211_world_regdom = &world_regdom; -#ifdef CONFIG_WIRELESS_OLD_REGULATORY -static char *ieee80211_regdom = "US"; -#else static char *ieee80211_regdom = "00"; -#endif module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); @@ -2287,22 +2283,12 @@ int regulatory_init(void) printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); print_regdomain_info(cfg80211_regdomain); - /* - * The old code still requests for a new regdomain and if - * you have CRDA you get it updated, otherwise you get - * stuck with the static values. Since "EU" is not a valid - * ISO / IEC 3166 alpha2 code we can't expect userpace to - * give us a regulatory domain for it. We need last_request - * iniitalized though so lets just send a request which we - * know will be ignored... this crap will be removed once - * OLD_REG dies. - */ - err = regulatory_hint_core(ieee80211_regdom); #else cfg80211_regdomain = cfg80211_world_regdom; - err = regulatory_hint_core(ieee80211_regdom); #endif + /* We always try to get an update for the static regdomain */ + err = regulatory_hint_core(cfg80211_regdomain->alpha2); if (err) { if (err == -ENOMEM) return err; @@ -2321,6 +2307,13 @@ int regulatory_init(void) #endif } + /* + * Finally, if the user set the module parameter treat it + * as a user hint. + */ + if (!is_world_regdom(ieee80211_regdom)) + regulatory_hint_user(ieee80211_regdom); + return 0; } -- cgit v1.1 From d4b1a6876f99ae1886cd254f649506af6692ac9f Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Thu, 16 Jul 2009 17:34:14 +0800 Subject: cfg80211: remove WARN_ON in __cfg80211_sme_scan_done cfg80211_sme_scan_done() can be called (by fullmac cards) with wdev->conn == NULL when CFG80211_SME_CONNECTING. We quit silently instead of WARN_ON in this case. Signed-off-by: Zhu Yi Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 10ed366..e7a8851 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -227,7 +227,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) if (wdev->sme_state != CFG80211_SME_CONNECTING) return; - if (WARN_ON(!wdev->conn)) + if (!wdev->conn) return; if (wdev->conn->state != CFG80211_CONN_SCANNING && -- cgit v1.1 From 25e83c490be421019997146bdec8645f5bcabcd1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 16 Jul 2009 11:39:04 +0200 Subject: cfg80211: don't optimise wext calls too much In the wext code I tried to not reconnect all the time when the user wasn't really sure what they were doing, like setting the BSSID back to the same value it was. However, this optimisation should only be done while associated so that setting the BSSID back to the same value that it was actually triggers a new association if not currently associated. To achieve, that, put the relevant code into the !IDLE case instead. Signed-off-by: Johannes Berg Tested-by: Kalle Valo Tested-by: Marcel Holtmann Signed-off-by: John W. Linville --- net/wireless/wext-sme.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 82e913a..4c689fd 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -72,13 +72,14 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, cfg80211_lock_rdev(rdev); wdev_lock(wdev); - if (wdev->wext.connect.channel == chan) { - err = 0; - goto out; - } - if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; + + if (wdev->wext.connect.channel == chan) { + err = 0; + goto out; + } + /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; @@ -164,13 +165,14 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, err = 0; - if (wdev->wext.connect.ssid && len && - len == wdev->wext.connect.ssid_len && - memcmp(wdev->wext.connect.ssid, ssid, len) == 0) - goto out; - if (wdev->sme_state != CFG80211_SME_IDLE) { bool event = true; + + if (wdev->wext.connect.ssid && len && + len == wdev->wext.connect.ssid_len && + memcmp(wdev->wext.connect.ssid, ssid, len) == 0) + goto out; + /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; @@ -244,17 +246,17 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); wdev_lock(wdev); - err = 0; - /* both automatic */ - if (!bssid && !wdev->wext.connect.bssid) - goto out; + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = 0; + /* both automatic */ + if (!bssid && !wdev->wext.connect.bssid) + goto out; - /* fixed already - and no change */ - if (wdev->wext.connect.bssid && bssid && - compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) - goto out; + /* fixed already - and no change */ + if (wdev->wext.connect.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) + goto out; - if (wdev->sme_state != CFG80211_SME_IDLE) { err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), dev, WLAN_REASON_DEAUTH_LEAVING, false); -- cgit v1.1 From a99d02483a40b9410d8a7af3b653ebc3f106280f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 19 Jul 2009 22:09:32 +0200 Subject: mac80211: do not monitor the connection while scanning mac80211 constantly monitors the connection to the associated AP in order to check if it is out of reach/dead. This is absolutely fine most of the time. Except when there is a scheduled scan for the whole neighborhood. After all this path could trigger while scanning on different channel. Or even worse: this AP probing triggers a WARN_ON in rate_lowest_index when the scan code did a band transition! ( http://www.kerneloops.org/raw.php?rawid=449304 ) Reported-by: Larry Finger Signed-off-by: Christian Lamparter Tested-by: Larry Finger Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e3b3156..523c0d9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2213,6 +2213,9 @@ static void ieee80211_sta_monitor_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.monitor_work); + if (sdata->local->sw_scanning || sdata->local->hw_scanning) + return; + ieee80211_mgd_probe_ap(sdata, false); } -- cgit v1.1 From f742880c9ca733b6c18bfaa0f5ad2a57f37180c2 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 19 Jul 2009 23:21:07 +0200 Subject: mac80211: fix spare warnings in driver-trace.h This patch fixes the following errors: driver-trace.h:148:1: error: cannot size expression driver-trace.h:148:1: error: cannot size expression [...] driver-trace.h:222:1: error: cannot size expression driver-trace.h:71:1: error: incompatible types for operation (<) driver-trace.h:71:1: left side has type void * driver-trace.h:71:1: right side has type int driver-trace.h:99:1: error: incompatible types for operation (<) driver-trace.h:99:1: left side has type void * driver-trace.h:99:1: right side has type int driver-trace.h:148:1: error: incompatible types for operation (<) driver-trace.h:148:1: left side has type void * driver-trace.h:148:1: right side has type int driver-trace.h:222:1: error: cannot size expression driver-trace.h:248:1: error: incompatible types for operation (<) driver-trace.h:248:1: left side has type void * driver-trace.h:248:1: right side has type int driver-trace.h:446:1: error: incompatible types for operation (<) driver-trace.h:446:1: left side has type void * driver-trace.h:446:1: right side has type int Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- net/mac80211/driver-trace.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 48c93d1..5a10da2 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -5,7 +5,7 @@ #include #include "ieee80211_i.h" -#ifndef CONFIG_MAC80211_DRIVER_API_TRACER +#if !defined(CONFIG_MAC80211_DRIVER_API_TRACER) || defined(__CHECKER__) #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} @@ -639,7 +639,7 @@ TRACE_EVENT(drv_ampdu_action, LOCAL_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret ) ); -#endif /* __MAC80211_DRIVER_TRACE */ +#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . -- cgit v1.1 From 1f00fca5c83c1bc5b4ca7e07f2a030bc39c130f2 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 20 Jul 2009 11:47:43 +0800 Subject: cfg80211: set_default_key only for WEP We invoke the cfg80211 set_default_key callback only for WEP key configuring. Signed-off-by: Zhu Yi Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e6731bf..c7351a9 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -531,7 +531,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, wdev->wext.keys->data[idx]; } - if (params->cipher != WLAN_CIPHER_SUITE_AES_CMAC && + if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104) && (tx_key || (!addr && wdev->wext.default_key == -1))) { if (wdev->current_bss) err = rdev->ops->set_default_key(&rdev->wiphy, -- cgit v1.1 From 3409ff7711bcf70390d5ba8ebde5d913b5266a45 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 20 Jul 2009 11:47:44 +0800 Subject: cfg80211: fix typo of IWEVASSOCRESPIE It should be IWEVASSOCREQIE instead. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/wireless/sme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index e7a8851..82de2d9 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -341,7 +341,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (req_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); } if (resp_ie && status == WLAN_STATUS_SUCCESS) { @@ -474,7 +474,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, if (req_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + wireless_send_event(wdev->netdev, IWEVASSOCREQIE, &wrqu, req_ie); } -- cgit v1.1 From 1e056665e878ce4f91dbfd594f4ebba49ea689c0 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 20 Jul 2009 16:12:57 +0800 Subject: cfg80211: avoid setting default_key if add_key fails In cfg80211_upload_connect_keys(), we call add_key, set_default_key and set_default_mgmt_key (if applicable) one by one. If one of these operations fails, we should stop calling the following functions. Because if the key is not added successfully, we should not set it as default key anyway. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/wireless/util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/util.c b/net/wireless/util.c index 4bab380..ba387d8 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -546,13 +546,17 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) if (!wdev->connect_keys->params[i].cipher) continue; if (rdev->ops->add_key(wdev->wiphy, dev, i, NULL, - &wdev->connect_keys->params[i])) + &wdev->connect_keys->params[i])) { printk(KERN_ERR "%s: failed to set key %d\n", dev->name, i); + continue; + } if (wdev->connect_keys->def == i) - if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) + if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) { printk(KERN_ERR "%s: failed to set defkey %d\n", dev->name, i); + continue; + } if (wdev->connect_keys->defmgmt == i) if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) printk(KERN_ERR "%s: failed to set mgtdef %d\n", -- cgit v1.1 From c4029083e2acb82229c43b791c07afb089d972ff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 17:43:30 +0200 Subject: net: export __dev_addr_sync/__dev_addr_unsync For mac80211, with the master netdev removal, we need to be able to sync a multicast address list onto another list that is not tracked within a netdev, so we need access to the functions doing that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/core/dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index dca8b50..d6c657e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3923,6 +3923,7 @@ int __dev_addr_sync(struct dev_addr_list **to, int *to_count, } return err; } +EXPORT_SYMBOL_GPL(__dev_addr_sync); void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count) @@ -3942,6 +3943,7 @@ void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, da = next; } } +EXPORT_SYMBOL_GPL(__dev_addr_unsync); /** * dev_unicast_sync - Synchronize device's unicast list to another device -- cgit v1.1 From 3b8d81e020f77c9da8b85b0685c8cd2ca7c7b150 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 17:43:56 +0200 Subject: mac80211: remove master netdev With the internal 'pending' queue system in place, we can simply put packets there instead of pushing them off to the master dev, getting rid of the master interface completely. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-tx.c | 3 - net/mac80211/debugfs.c | 2 +- net/mac80211/ieee80211_i.h | 19 ++- net/mac80211/iface.c | 43 ++++--- net/mac80211/main.c | 120 +----------------- net/mac80211/rate.c | 2 +- net/mac80211/rx.c | 16 ++- net/mac80211/scan.c | 19 ++- net/mac80211/tx.c | 299 +++++++++++++++++++-------------------------- net/mac80211/util.c | 52 ++------ net/mac80211/wme.c | 6 +- net/mac80211/wme.h | 3 +- 12 files changed, 192 insertions(+), 392 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 9e5762a..1958c7c 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -383,9 +383,6 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local, 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, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 6c439cd..96991b6 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -175,7 +175,7 @@ static ssize_t queues_read(struct file *file, char __user *user_buf, for (q = 0; q < local->hw.queues; q++) res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q, local->queue_stop_reasons[q], - __netif_subqueue_stopped(local->mdev, q)); + skb_queue_len(&local->pending[q])); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return simple_read_from_buffer(user_buf, count, ppos, buf, res); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a34bca2..6a01771 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -567,14 +567,9 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_CSA, IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, - IEEE80211_QUEUE_STOP_REASON_PENDING, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, }; -struct ieee80211_master_priv { - struct ieee80211_local *local; -}; - struct ieee80211_local { /* embed the driver visible part. * don't cast (use the static inlines below), but we keep @@ -587,13 +582,20 @@ struct ieee80211_local { /* 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; /* number of interfaces with corresponding FIF_ flags */ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss; unsigned int filter_flags; /* FIF_* */ struct iw_statistics wstats; + + /* protects the aggregated multicast list and filter calls */ + spinlock_t filter_lock; + + /* aggregated multicast list */ + struct dev_addr_list *mc_list; + int mc_count; + bool tim_in_locked_section; /* see ieee80211_beacon_get() */ /* @@ -813,10 +815,6 @@ struct ieee80211_local { static inline struct ieee80211_sub_if_data * IEEE80211_DEV_TO_SUB_IF(struct net_device *dev) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - - BUG_ON(!local || local->mdev == dev); - return netdev_priv(dev); } @@ -996,7 +994,6 @@ void ieee80211_recalc_idle(struct ieee80211_local *local); /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); void ieee80211_tx_pending(unsigned long data); -int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev); int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 090aa5a..2f797a8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -190,10 +190,6 @@ static int ieee80211_open(struct net_device *dev) ETH_ALEN); } - if (compare_ether_addr(null_addr, local->mdev->dev_addr) == 0) - memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, - ETH_ALEN); - /* * Validate the MAC address for this device. */ @@ -229,9 +225,9 @@ static int ieee80211_open(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - netif_addr_unlock_bh(local->mdev); + spin_unlock_bh(&local->filter_lock); break; default: conf.vif = &sdata->vif; @@ -243,9 +239,9 @@ static int ieee80211_open(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) { local->fif_other_bss++; - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - netif_addr_unlock_bh(local->mdev); + spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); } @@ -279,10 +275,6 @@ static int ieee80211_open(struct net_device *dev) } if (local->open_count == 0) { - res = dev_open(local->mdev); - WARN_ON(res); - if (res) - goto err_del_interface; tasklet_enable(&local->tx_pending_tasklet); tasklet_enable(&local->tasklet); } @@ -393,7 +385,14 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_dec(&local->iff_promiscs); - dev_mc_unsync(local->mdev, dev); + netif_addr_lock_bh(dev); + spin_lock_bh(&local->filter_lock); + __dev_addr_unsync(&local->mc_list, &local->mc_count, + &dev->mc_list, &dev->mc_count); + ieee80211_configure_filter(local); + spin_unlock_bh(&local->filter_lock); + netif_addr_unlock_bh(dev); + del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -442,9 +441,9 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - netif_addr_unlock_bh(local->mdev); + spin_unlock_bh(&local->filter_lock); break; case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); @@ -487,9 +486,9 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; atomic_dec(&local->iff_allmultis); - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - netif_addr_unlock_bh(local->mdev); + spin_unlock_bh(&local->filter_lock); ieee80211_stop_mesh(sdata); } @@ -535,9 +534,6 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { - if (netif_running(local->mdev)) - dev_close(local->mdev); - drv_stop(local); ieee80211_led_radio(local, false); @@ -584,8 +580,11 @@ static void ieee80211_set_multicast_list(struct net_device *dev) atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } - - dev_mc_sync(local->mdev, dev); + spin_lock_bh(&local->filter_lock); + __dev_addr_sync(&local->mc_list, &local->mc_count, + &dev->mc_list, &dev->mc_count); + ieee80211_configure_filter(local); + spin_unlock_bh(&local->filter_lock); } /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5b69f5f..3234f37 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -83,75 +83,14 @@ void ieee80211_configure_filter(struct ieee80211_local *local) new_flags |= (1<<31); drv_configure_filter(local, changed_flags, &new_flags, - local->mdev->mc_count, - local->mdev->mc_list); + local->mc_count, + local->mc_list); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } -/* master interface */ - -static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr) -{ - memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ - return ETH_ALEN; -} - -static const struct header_ops ieee80211_header_ops = { - .create = eth_header, - .parse = header_parse_80211, - .rebuild = eth_rebuild_header, - .cache = eth_header_cache, - .cache_update = eth_header_cache_update, -}; - -static int ieee80211_master_open(struct net_device *dev) -{ - struct ieee80211_master_priv *mpriv = netdev_priv(dev); - struct ieee80211_local *local = mpriv->local; - struct ieee80211_sub_if_data *sdata; - int res = -EOPNOTSUPP; - - /* we hold the RTNL here so can safely walk the list */ - list_for_each_entry(sdata, &local->interfaces, list) { - if (netif_running(sdata->dev)) { - res = 0; - break; - } - } - - if (res) - return res; - - netif_tx_start_all_queues(local->mdev); - - return 0; -} - -static int ieee80211_master_stop(struct net_device *dev) -{ - struct ieee80211_master_priv *mpriv = netdev_priv(dev); - struct ieee80211_local *local = mpriv->local; - struct ieee80211_sub_if_data *sdata; - - /* we hold the RTNL here so can safely walk the list */ - list_for_each_entry(sdata, &local->interfaces, list) - if (netif_running(sdata->dev)) - dev_close(sdata->dev); - - return 0; -} - -static void ieee80211_master_set_multicast_list(struct net_device *dev) -{ - struct ieee80211_master_priv *mpriv = netdev_priv(dev); - struct ieee80211_local *local = mpriv->local; - - ieee80211_configure_filter(local); -} - int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -310,7 +249,6 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int tmp; - skb->dev = local->mdev; skb->pkt_type = IEEE80211_TX_STATUS_MSG; skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ? &local->skb_queue : &local->skb_queue_unreliable, skb); @@ -716,7 +654,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, mutex_init(&local->scan_mtx); spin_lock_init(&local->key_lock); - + spin_lock_init(&local->filter_lock); spin_lock_init(&local->queue_stop_reason_lock); INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); @@ -752,30 +690,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, } EXPORT_SYMBOL(ieee80211_alloc_hw); -static const struct net_device_ops ieee80211_master_ops = { - .ndo_start_xmit = ieee80211_master_start_xmit, - .ndo_open = ieee80211_master_open, - .ndo_stop = ieee80211_master_stop, - .ndo_set_multicast_list = ieee80211_master_set_multicast_list, - .ndo_select_queue = ieee80211_select_queue, -}; - -static void ieee80211_master_setup(struct net_device *mdev) -{ - mdev->type = ARPHRD_IEEE80211; - mdev->netdev_ops = &ieee80211_master_ops; - mdev->header_ops = &ieee80211_header_ops; - mdev->tx_queue_len = 1000; - mdev->addr_len = ETH_ALEN; -} - int ieee80211_register_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); int result; enum ieee80211_band band; - struct net_device *mdev; - struct ieee80211_master_priv *mpriv; int channels, i, j, max_bitrates; bool supp_ht; static const u32 cipher_suites[] = { @@ -874,16 +793,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv), - "wmaster%d", ieee80211_master_setup, - hw->queues); - if (!mdev) - goto fail_mdev_alloc; - - mpriv = netdev_priv(mdev); - mpriv->local = local; - local->mdev = mdev; - local->hw.workqueue = create_singlethread_workqueue(wiphy_name(local->hw.wiphy)); if (!local->hw.workqueue) { @@ -918,17 +827,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } rtnl_lock(); - result = dev_alloc_name(local->mdev, local->mdev->name); - if (result < 0) - goto fail_dev; - - memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); - SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy)); - local->mdev->features |= NETIF_F_NETNS_LOCAL; - - result = register_netdevice(local->mdev); - if (result < 0) - goto fail_dev; result = ieee80211_init_rate_ctrl_alg(local, hw->rate_control_algorithm); @@ -981,9 +879,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_exit(local); ieee80211_remove_interfaces(local); fail_rate: - unregister_netdevice(local->mdev); - local->mdev = NULL; - fail_dev: rtnl_unlock(); ieee80211_wep_free(local); fail_wep: @@ -992,9 +887,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) debugfs_hw_del(local); destroy_workqueue(local->hw.workqueue); fail_workqueue: - if (local->mdev) - free_netdev(local->mdev); - fail_mdev_alloc: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: kfree(local->int_scan_req.channels); @@ -1019,13 +911,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) * because the driver cannot be handing us frames any * more and the tasklet is killed. */ - - /* First, we remove all virtual interfaces. */ ieee80211_remove_interfaces(local); - /* then, finally, remove the master interface */ - unregister_netdevice(local->mdev); - rtnl_unlock(); ieee80211_clear_tx_pending(local); @@ -1044,7 +931,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) wiphy_unregister(local->hw.wiphy); 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/rate.c b/net/mac80211/rate.c index 8ac7a98..b33efc4 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -287,7 +287,7 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, struct rate_control_ref *ref, *old; ASSERT_RTNL(); - if (local->open_count || netif_running(local->mdev)) + if (local->open_count) return -EBUSY; ref = rate_control_alloc(name, local); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b513fb7..7f33f77 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1478,6 +1478,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) struct ieee80211s_hdr *mesh_hdr; unsigned int hdrlen; struct sk_buff *skb = rx->skb, *fwd_skb; + struct ieee80211_local *local = rx->local; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -1520,6 +1521,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) dropped_frames_ttl); else { struct ieee80211_hdr *fwd_hdr; + struct ieee80211_tx_info *info; + fwd_skb = skb_copy(skb, GFP_ATOMIC); if (!fwd_skb && net_ratelimit()) @@ -1533,9 +1536,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) */ memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN); memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN); - fwd_skb->dev = rx->local->mdev; + info = IEEE80211_SKB_CB(fwd_skb); + memset(info, 0, sizeof(*info)); + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; fwd_skb->iif = rx->dev->ifindex; - dev_queue_xmit(fwd_skb); + ieee80211_select_queue(local, fwd_skb); + ieee80211_add_pending_skb(local, fwd_skb); } } @@ -1803,8 +1809,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; } -static void ieee80211_rx_michael_mic_report(struct net_device *dev, - struct ieee80211_hdr *hdr, +static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr, struct ieee80211_rx_data *rx) { int keyidx; @@ -2114,7 +2119,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, } if ((status->flag & RX_FLAG_MMIC_ERROR)) { - ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx); + ieee80211_rx_michael_mic_report(hdr, &rx); return; } @@ -2483,7 +2488,6 @@ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); - skb->dev = local->mdev; skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5f4f786..7482065 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -294,16 +294,13 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (was_hw_scan) goto done; - netif_tx_lock_bh(local->mdev); - netif_addr_lock(local->mdev); + spin_lock_bh(&local->filter_lock); local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, &local->filter_flags, - local->mdev->mc_count, - local->mdev->mc_list); - - netif_addr_unlock(local->mdev); - netif_tx_unlock_bh(local->mdev); + local->mc_count, + local->mc_list); + spin_unlock_bh(&local->filter_lock); drv_sw_scan_complete(local); @@ -382,13 +379,13 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->scan_state = SCAN_SET_CHANNEL; local->scan_channel_idx = 0; - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, &local->filter_flags, - local->mdev->mc_count, - local->mdev->mc_list); - netif_addr_unlock_bh(local->mdev); + local->mc_count, + local->mc_list); + spin_unlock_bh(&local->filter_lock); /* TODO: start scan as soon as all nullfunc frames are ACKed */ queue_delayed_work(local->hw.workqueue, &local->scan_work, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f3efd4f..7adaeb2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -451,7 +451,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - if (unlikely(tx->skb->do_not_encrypt)) + if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) tx->key = NULL; else if (tx->sta && (key = rcu_dereference(tx->sta->key))) tx->key = key; @@ -497,7 +497,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) } if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) - tx->skb->do_not_encrypt = 1; + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; return TX_CONTINUE; } @@ -774,9 +774,7 @@ static int ieee80211_fragment(struct ieee80211_local *local, memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); skb_copy_queue_mapping(tmp, skb); tmp->priority = skb->priority; - tmp->do_not_encrypt = skb->do_not_encrypt; tmp->dev = skb->dev; - tmp->iif = skb->iif; /* copy header and data */ memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen); @@ -804,7 +802,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) /* * Warn when submitting a fragmented A-MPDU frame and drop it. - * This scenario is handled in __ieee80211_tx_prepare but extra + * This scenario is handled in ieee80211_tx_prepare but extra * caution taken here as fragmented ampdu may cause Tx stop. */ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) @@ -943,11 +941,12 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_supported_band *sband; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len); sband = tx->local->hw.wiphy->bands[tx->channel->band]; - skb->do_not_encrypt = 1; + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; /* @@ -985,7 +984,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, skb_trim(skb, skb->len - FCS_LEN); } if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) - tx->skb->do_not_encrypt = 0; + info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) tx->flags |= IEEE80211_TX_FRAGMENTED; break; @@ -1018,13 +1017,12 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, * initialises @tx */ static ieee80211_tx_result -__ieee80211_tx_prepare(struct ieee80211_tx_data *tx, - struct sk_buff *skb, - struct net_device *dev) +ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, + struct ieee80211_tx_data *tx, + struct sk_buff *skb) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_local *local = sdata->local; 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; @@ -1032,9 +1030,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, memset(tx, 0, sizeof(*tx)); tx->skb = skb; - tx->dev = dev; /* use original interface */ + tx->dev = sdata->dev; /* use original interface */ tx->local = local; - tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev); + tx->sdata = sdata; tx->channel = local->hw.conf.channel; /* * Set this flag (used below to indicate "automatic fragmentation"), @@ -1043,7 +1041,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, tx->flags |= IEEE80211_TX_FRAGMENTED; /* process and remove the injection radiotap header */ - sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) { if (!__ieee80211_parse_tx_radiotap(tx, skb)) return TX_DROP; @@ -1139,50 +1136,28 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, return TX_CONTINUE; } -/* - * NB: @tx is uninitialised when passed in here - */ -static int ieee80211_tx_prepare(struct ieee80211_local *local, - struct ieee80211_tx_data *tx, - struct sk_buff *skb) -{ - struct net_device *dev; - - dev = dev_get_by_index(&init_net, skb->iif); - if (unlikely(dev && !is_ieee80211_device(local, dev))) { - dev_put(dev); - dev = NULL; - } - if (unlikely(!dev)) - return -ENODEV; - /* - * 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; -} - static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, - struct sta_info *sta) + struct sta_info *sta, + bool txpending) { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; + unsigned long flags; int ret, len; bool fragm = false; - local->mdev->trans_start = jiffies; - while (skb) { - if (ieee80211_queue_stopped(&local->hw, - skb_get_queue_mapping(skb))) - return IEEE80211_TX_PENDING; + int q = skb_get_queue_mapping(skb); + + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + ret = IEEE80211_TX_OK; + if (local->queue_stop_reasons[q] || + (!txpending && !skb_queue_empty(&local->pending[q]))) + ret = IEEE80211_TX_PENDING; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if (ret != IEEE80211_TX_OK) + return ret; info = IEEE80211_SKB_CB(skb); @@ -1254,10 +1229,10 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, - bool txpending) +static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, bool txpending) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1268,8 +1243,6 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, queue = skb_get_queue_mapping(skb); - WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue])); - if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); return; @@ -1278,7 +1251,7 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, rcu_read_lock(); /* initialises tx */ - res_prepare = __ieee80211_tx_prepare(&tx, skb, dev); + res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); @@ -1297,7 +1270,7 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, retries = 0; retry: - ret = __ieee80211_tx(local, &tx.skb, tx.sta); + ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); switch (ret) { case IEEE80211_TX_OK: break; @@ -1315,34 +1288,35 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - if (__netif_subqueue_stopped(local->mdev, queue)) { + if (local->queue_stop_reasons[queue] || + !skb_queue_empty(&local->pending[queue])) { + /* + * if queue is stopped, queue up frames for later + * transmission from the tasklet + */ do { next = skb->next; skb->next = NULL; if (unlikely(txpending)) - skb_queue_head(&local->pending[queue], - skb); + __skb_queue_head(&local->pending[queue], + skb); else - skb_queue_tail(&local->pending[queue], - skb); + __skb_queue_tail(&local->pending[queue], + skb); } while ((skb = next)); - /* - * Make sure nobody will enable the queue on us - * (without going through the tasklet) nor disable the - * netdev queue underneath the pending handling code. - */ - __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, - &local->queue_stop_reasons[queue]); - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } else { + /* + * otherwise retry, but this is a race condition or + * a driver bug (which we warn about if it persists) + */ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); retries++; - if (WARN(retries > 10, "tx refused but queue active")) + if (WARN(retries > 10, "tx refused but queue active\n")) goto drop; goto retry; } @@ -1403,14 +1377,13 @@ static int ieee80211_skb_resize(struct ieee80211_local *local, return 0; } -int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) +static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { - struct ieee80211_master_priv *mpriv = netdev_priv(dev); - struct ieee80211_local *local = mpriv->local; + struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct net_device *odev = NULL; - struct ieee80211_sub_if_data *osdata; + struct ieee80211_sub_if_data *tmp_sdata; int headroom; bool may_encrypt; enum { @@ -1419,20 +1392,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) UNKNOWN_ADDRESS, } monitor_iface = NOT_MONITOR; - if (skb->iif) - odev = dev_get_by_index(&init_net, skb->iif); - if (unlikely(odev && !is_ieee80211_device(local, odev))) { - dev_put(odev); - odev = NULL; - } - if (unlikely(!odev)) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - printk(KERN_DEBUG "%s: Discarded packet with nonexistent " - "originating device\n", dev->name); -#endif - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } + dev_hold(sdata->dev); if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && local->hw.conf.dynamic_ps_timeout > 0 && @@ -1448,26 +1408,21 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); } - memset(info, 0, sizeof(*info)); - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - osdata = IEEE80211_DEV_TO_SUB_IF(odev); - - if (ieee80211_vif_is_mesh(&osdata->vif) && + if (ieee80211_vif_is_mesh(&sdata->vif) && ieee80211_is_data(hdr->frame_control)) { if (is_multicast_ether_addr(hdr->addr3)) memcpy(hdr->addr1, hdr->addr3, ETH_ALEN); else - if (mesh_nexthop_lookup(skb, osdata)) { - dev_put(odev); - return NETDEV_TX_OK; + if (mesh_nexthop_lookup(skb, sdata)) { + dev_put(sdata->dev); + return; } - if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0) - IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh, - fwded_frames); - } else if (unlikely(osdata->vif.type == NL80211_IFTYPE_MONITOR)) { - struct ieee80211_sub_if_data *sdata; + if (memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) != 0) + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_frames); + } else if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { int hdrlen; u16 len_rthdr; @@ -1491,19 +1446,17 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) */ rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, + list_for_each_entry_rcu(tmp_sdata, &local->interfaces, list) { - if (!netif_running(sdata->dev)) + if (!netif_running(tmp_sdata->dev)) continue; - if (sdata->vif.type != NL80211_IFTYPE_AP) + if (tmp_sdata->vif.type != NL80211_IFTYPE_AP) continue; - if (compare_ether_addr(sdata->dev->dev_addr, + if (compare_ether_addr(tmp_sdata->dev->dev_addr, hdr->addr2)) { - dev_hold(sdata->dev); - dev_put(odev); - osdata = sdata; - odev = osdata->dev; - skb->iif = sdata->dev->ifindex; + dev_hold(tmp_sdata->dev); + dev_put(sdata->dev); + sdata = tmp_sdata; monitor_iface = FOUND_SDATA; break; } @@ -1512,31 +1465,31 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) } } - may_encrypt = !skb->do_not_encrypt; + may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT); - headroom = osdata->local->tx_headroom; + headroom = local->tx_headroom; if (may_encrypt) headroom += IEEE80211_ENCRYPT_HEADROOM; headroom -= skb_headroom(skb); headroom = max_t(int, 0, headroom); - if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) { + if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) { dev_kfree_skb(skb); - dev_put(odev); - return NETDEV_TX_OK; + dev_put(sdata->dev); + return; } - if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN) - osdata = container_of(osdata->bss, - struct ieee80211_sub_if_data, - u.ap); + tmp_sdata = sdata; + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + tmp_sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); if (likely(monitor_iface != UNKNOWN_ADDRESS)) - info->control.vif = &osdata->vif; - - ieee80211_tx(odev, skb, false); - dev_put(odev); + info->control.vif = &tmp_sdata->vif; - return NETDEV_TX_OK; + ieee80211_select_queue(local, skb); + ieee80211_tx(sdata, skb, false); + dev_put(sdata->dev); } int ieee80211_monitor_start_xmit(struct sk_buff *skb, @@ -1546,6 +1499,7 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct ieee80211_channel *chan = local->hw.conf.channel; struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u16 len_rthdr; /* @@ -1583,15 +1537,9 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, if (unlikely(skb->len < len_rthdr)) goto fail; /* skb too short for claimed rt header extent */ - skb->dev = local->mdev; - /* needed because we set skb device to master */ skb->iif = dev->ifindex; - /* sometimes we do encrypt injected frames, will be fixed - * up in radiotap parser if not wanted */ - skb->do_not_encrypt = 0; - /* * fix up the pointers accounting for the radiotap * header still being in there. We are being given @@ -1606,8 +1554,10 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, skb_set_network_header(skb, len_rthdr); skb_set_transport_header(skb, len_rthdr); - /* pass the radiotap header up to the next stage intact */ - dev_queue_xmit(skb); + memset(info, 0, sizeof(*info)); + + /* pass the radiotap header up to xmit */ + ieee80211_xmit(IEEE80211_DEV_TO_SUB_IF(dev), skb); return NETDEV_TX_OK; fail: @@ -1635,6 +1585,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = NETDEV_TX_BUSY, head_need; u16 ethertype, hdrlen, meshhdrlen = 0; __le16 fc; @@ -1864,7 +1815,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, skb->iif = dev->ifindex; - skb->dev = local->mdev; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; @@ -1875,8 +1825,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, skb_set_network_header(skb, nh_pos); skb_set_transport_header(skb, h_pos); + memset(info, 0, sizeof(*info)); + dev->trans_start = jiffies; - dev_queue_xmit(skb); + ieee80211_xmit(sdata, skb); return NETDEV_TX_OK; @@ -1918,7 +1870,6 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, 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, @@ -1932,12 +1883,13 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, } if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { - ieee80211_tx(dev, skb, true); + /* do not use sdata, it may have been changed above */ + ieee80211_tx(IEEE80211_DEV_TO_SUB_IF(dev), skb, true); } else { hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(local, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta); + ret = __ieee80211_tx(local, &skb, sta, true); if (ret != IEEE80211_TX_OK) result = false; } @@ -1949,59 +1901,43 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, } /* - * Transmit all pending packets. Called from tasklet, locks master device - * TX lock so that no new packets can come in. + * Transmit all pending packets. Called from tasklet. */ void ieee80211_tx_pending(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *)data; - struct net_device *dev = local->mdev; unsigned long flags; int i; - bool next; + bool txok; rcu_read_lock(); - netif_tx_lock_bh(dev); + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < local->hw.queues; i++) { /* * If queue is stopped by something other than due to pending * frames, or we have no pending frames, proceed to next queue. */ - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - next = false; - if (local->queue_stop_reasons[i] != - BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) || + if (local->queue_stop_reasons[i] || skb_queue_empty(&local->pending[i])) - next = true; - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - - if (next) continue; - /* - * start the queue now to allow processing our packets, - * we're under the tx lock here anyway so nothing will - * happen as a result of this - */ - netif_start_subqueue(local->mdev, i); - while (!skb_queue_empty(&local->pending[i])) { - struct sk_buff *skb = skb_dequeue(&local->pending[i]); - - if (!ieee80211_tx_pending_skb(local, skb)) { - skb_queue_head(&local->pending[i], skb); + struct sk_buff *skb = __skb_dequeue(&local->pending[i]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + + txok = ieee80211_tx_pending_skb(local, skb); + if (!txok) + __skb_queue_head(&local->pending[i], skb); + spin_lock_irqsave(&local->queue_stop_reason_lock, + flags); + if (!txok) break; - } } - - /* Start regular packet processing again. */ - if (skb_queue_empty(&local->pending[i])) - ieee80211_wake_queue_by_reason(&local->hw, i, - IEEE80211_QUEUE_STOP_REASON_PENDING); } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - netif_tx_unlock_bh(dev); rcu_read_unlock(); } @@ -2176,8 +2112,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); - skb->do_not_encrypt = 1; - + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; info->band = band; /* * XXX: For now, always use the lowest rate @@ -2248,9 +2183,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, sdata = vif_to_sdata(vif); bss = &sdata->u.ap; - if (!bss) - return NULL; - rcu_read_lock(); beacon = rcu_dereference(bss->beacon); @@ -2276,7 +2208,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, cpu_to_le16(IEEE80211_FCTL_MOREDATA); } - if (!ieee80211_tx_prepare(local, &tx, skb)) + if (!ieee80211_tx_prepare(sdata, &tx, skb)) break; dev_kfree_skb_any(skb); } @@ -2296,3 +2228,18 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, return skb; } EXPORT_SYMBOL(ieee80211_get_buffered_bc); + +void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, + int encrypt) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, 0); + skb_set_transport_header(skb, 0); + + skb->iif = sdata->dev->ifindex; + if (!encrypt) + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + + ieee80211_xmit(sdata, skb); +} diff --git a/net/mac80211/util.c b/net/mac80211/util.c index dbf66b5..7fc5584 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -275,16 +275,12 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, __clear_bit(reason, &local->queue_stop_reasons[queue]); - if (!skb_queue_empty(&local->pending[queue]) && - local->queue_stop_reasons[queue] == - BIT(IEEE80211_QUEUE_STOP_REASON_PENDING)) - tasklet_schedule(&local->tx_pending_tasklet); - if (local->queue_stop_reasons[queue] != 0) /* someone still has this queue stopped */ return; - netif_wake_subqueue(local->mdev, queue); + if (!skb_queue_empty(&local->pending[queue])) + tasklet_schedule(&local->tx_pending_tasklet); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -313,14 +309,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, if (WARN_ON(queue >= hw->queues)) return; - /* - * Only stop if it was previously running, this is necessary - * for correct pending packets handling because there we may - * start (but not wake) the queue and rely on that. - */ - if (!local->queue_stop_reasons[queue]) - netif_stop_subqueue(local->mdev, queue); - __set_bit(reason, &local->queue_stop_reasons[queue]); } @@ -351,8 +339,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); - __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_PENDING); - skb_queue_tail(&local->pending[queue], skb); + __skb_queue_tail(&local->pending[queue], skb); __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -373,16 +360,12 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, while ((skb = skb_dequeue(skbs))) { ret++; queue = skb_get_queue_mapping(skb); - skb_queue_tail(&local->pending[queue], skb); + __skb_queue_tail(&local->pending[queue], skb); } - for (i = 0; i < hw->queues; i++) { - if (ret) - __ieee80211_stop_queue(hw, i, - IEEE80211_QUEUE_STOP_REASON_PENDING); + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); - } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return ret; @@ -413,11 +396,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; + int ret; if (WARN_ON(queue >= hw->queues)) return true; - return __netif_subqueue_stopped(local->mdev, queue); + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + ret = !!local->queue_stop_reasons[queue]; + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + return ret; } EXPORT_SYMBOL(ieee80211_queue_stopped); @@ -761,20 +749,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, ieee80211_set_wmm_default(sdata); } -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - int encrypt) -{ - skb->dev = sdata->local->mdev; - skb_set_mac_header(skb, 0); - skb_set_network_header(skb, 0); - skb_set_transport_header(skb, 0); - - skb->iif = sdata->dev->ifindex; - skb->do_not_encrypt = !encrypt; - - dev_queue_xmit(skb); -} - u32 ieee80211_mandatory_rates(struct ieee80211_local *local, enum ieee80211_band band) { @@ -1049,9 +1023,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* reconfigure hardware */ ieee80211_hw_config(local, ~0); - netif_addr_lock_bh(local->mdev); + spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - netif_addr_unlock_bh(local->mdev); + spin_unlock_bh(&local->filter_lock); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 116a923..b19b769 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -85,10 +85,8 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) return ieee802_1d_to_ac[skb->priority]; } -u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) +void ieee80211_select_queue(struct ieee80211_local *local, struct sk_buff *skb) { - struct ieee80211_master_priv *mpriv = netdev_priv(dev); - struct ieee80211_local *local = mpriv->local; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 queue; u8 tid; @@ -113,5 +111,5 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) *p = 0; } - return queue; + skb_set_queue_mapping(skb, queue); } diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 7520d2e..d4fd87c 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -20,6 +20,7 @@ extern const int ieee802_1d_to_ac[8]; -u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb); +void ieee80211_select_queue(struct ieee80211_local *local, + struct sk_buff *skb); #endif /* _WME_H */ -- cgit v1.1 From 72bce62775db0315511474e8d8f8e25d25b48366 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 17:45:28 +0200 Subject: net: remove unused skb->do_not_encrypt mac80211 required this due to the master netdev, but now it can put all information into skb->cb and this can go. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/core/skbuff.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9e0597d..80a9616 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -559,9 +559,6 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #endif #endif new->vlan_tci = old->vlan_tci; -#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE) - new->do_not_encrypt = old->do_not_encrypt; -#endif skb_copy_secmark(new, old); } -- cgit v1.1 From 3d34deb6737b1ae1f8b7817b57d603807f5d88ea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 18 Jun 2009 17:25:11 +0200 Subject: mac80211: fix ieee80211_xmit call context ieee80211_xmit() cannot be called with tasklets enabled because it is normally called from within a tasklet. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7adaeb2..a204092 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2241,5 +2241,12 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, if (!encrypt) info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + /* + * The other path calling ieee80211_xmit is from the tasklet, + * and while we can handle concurrent transmissions locking + * requirements are that we do not come into tx with bhs on. + */ + local_bh_disable(); ieee80211_xmit(sdata, skb); + local_bh_enable(); } -- cgit v1.1 From 249b405cf8145da8a74b70544ae1079d244bdb00 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 7 Jul 2009 10:55:03 -0700 Subject: mac80211: Fix regression in mesh forwarding path. The removal of the master netdev broke the mesh forwarding path. This patch fixes it by using the new internal 'pending' queue. As a result of this change, mesh forwarding no longer does the inefficient 802.11 -> 802.3 -> 802.11 conversion that was done before. [Changes since v1] Suggested by Johannes: - Select queue before adding to mpath queue - ieee80211_add_pending_skb -> ieee80211_add_pending_skbs - Remove unnecessary header wme.h Signed-off-by: Javier Cardona Signed-off-by: Andrey Yurovsky Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh_hwmp.c | 3 ++- net/mac80211/mesh_pathtbl.c | 8 +++----- net/mac80211/rx.c | 17 +++++++++++++++-- net/mac80211/tx.c | 3 --- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 8e86e91..e93c37e 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -784,7 +784,6 @@ int mesh_nexthop_lookup(struct sk_buff *skb, mesh_path_add(dst_addr, sdata); mpath = mesh_path_lookup(dst_addr, sdata); if (!mpath) { - dev_kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; err = -ENOSPC; goto endlookup; @@ -804,6 +803,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, memcpy(hdr->addr1, mpath->next_hop->sta.addr, ETH_ALEN); } else { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!(mpath->flags & MESH_PATH_RESOLVING)) { /* Start discovery only if it is not running yet */ mesh_queue_preq(mpath, PREQ_Q_F_START); @@ -815,6 +815,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, skb_unlink(skb_to_free, &mpath->frame_queue); } + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; skb_queue_tail(&mpath->frame_queue, skb); if (skb_to_free) mesh_path_discard_frame(skb_to_free, sdata); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index f0304bf..04b9e4d 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -499,11 +499,9 @@ enddel: */ void mesh_path_tx_pending(struct mesh_path *mpath) { - struct sk_buff *skb; - - while ((skb = skb_dequeue(&mpath->frame_queue)) && - (mpath->flags & MESH_PATH_ACTIVE)) - dev_queue_xmit(skb); + if (mpath->flags & MESH_PATH_ACTIVE) + ieee80211_add_pending_skbs(mpath->sdata->local, + &mpath->frame_queue); } /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7f33f77..66c797c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1479,10 +1479,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) unsigned int hdrlen; struct sk_buff *skb = rx->skb, *fwd_skb; struct ieee80211_local *local = rx->local; + struct ieee80211_sub_if_data *sdata; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); if (!ieee80211_is_data(hdr->frame_control)) return RX_CONTINUE; @@ -1492,10 +1494,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){ - struct ieee80211_sub_if_data *sdata; struct mesh_path *mppath; - sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); rcu_read_lock(); mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata); if (!mppath) { @@ -1541,6 +1541,19 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; fwd_skb->iif = rx->dev->ifindex; ieee80211_select_queue(local, fwd_skb); + if (is_multicast_ether_addr(fwd_hdr->addr3)) + memcpy(fwd_hdr->addr1, fwd_hdr->addr3, + ETH_ALEN); + else { + int err = mesh_nexthop_lookup(fwd_skb, sdata); + /* Failed to immediately resolve next hop: + * fwded frame was dropped or will be added + * later to the pending skb queue. */ + if (err) + return RX_DROP_MONITOR; + } + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_frames); ieee80211_add_pending_skb(local, fwd_skb); } } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a204092..2572509 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1419,9 +1419,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, dev_put(sdata->dev); return; } - if (memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) != 0) - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_frames); } else if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { int hdrlen; u16 len_rthdr; -- cgit v1.1 From 421d20a3dccb3670d4b2e8d3e4cef6327530f30d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 26 Jul 2009 13:39:10 -0700 Subject: phonet: Fix build. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by Oliver Hartkopp: net/phonet/pn_dev.c: In function ‘phonet_init_net’: net/phonet/pn_dev.c:221: error: implicit declaration of function ‘proc_net_fops_create’ net/phonet/pn_dev.c: In function ‘phonet_exit_net’: net/phonet/pn_dev.c:242: error: implicit declaration of function ‘proc_net_remove’ Signed-off-by: David S. Miller --- net/phonet/pn_dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 5107b79..6b30cc1 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include -- cgit v1.1 From 1896e61ff7cc1c9dd0d8b1cf4a9426a0f7217a20 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Wed, 22 Jul 2009 13:38:22 +0000 Subject: ethtool: device independent rx_csum and get_flags routines This helps avoid error messages with ethtool -k on devices that don't provide device specific routines. Signed-off-by: Sridhar Samudrala ------------------------------------------------------------------ Signed-off-by: David S. Miller --- net/core/ethtool.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/ethtool.c b/net/core/ethtool.c index d9d5160..cf36ff4 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -30,6 +30,11 @@ u32 ethtool_op_get_link(struct net_device *dev) return netif_carrier_ok(dev) ? 1 : 0; } +u32 ethtool_op_get_rx_csum(struct net_device *dev) +{ + return (dev->features & NETIF_F_ALL_CSUM) != 0; +} + u32 ethtool_op_get_tx_csum(struct net_device *dev) { return (dev->features & NETIF_F_ALL_CSUM) != 0; @@ -1004,7 +1009,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) break; case ETHTOOL_GRXCSUM: rc = ethtool_get_value(dev, useraddr, ethcmd, - dev->ethtool_ops->get_rx_csum); + (dev->ethtool_ops->get_rx_csum ? + dev->ethtool_ops->get_rx_csum : + ethtool_op_get_rx_csum)); break; case ETHTOOL_SRXCSUM: rc = ethtool_set_rx_csum(dev, useraddr); @@ -1068,7 +1075,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) break; case ETHTOOL_GFLAGS: rc = ethtool_get_value(dev, useraddr, ethcmd, - dev->ethtool_ops->get_flags); + (dev->ethtool_ops->get_flags ? + dev->ethtool_ops->get_flags : + ethtool_op_get_flags)); break; case ETHTOOL_SFLAGS: rc = ethtool_set_value(dev, useraddr, -- cgit v1.1 From 59e57f4417507b1e71f6e5af3eb7e68e6477ac94 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 27 Jul 2009 08:03:18 -0700 Subject: phonet: phonet_device_get() fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net/phonet/pn_dev.c: In function `phonet_device_get': net/phonet/pn_dev.c:99: warning: 'dev' might be used uninitialized in this function Signed-off-by: Eric Dumazet Acked-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/pn_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 6b30cc1..5ae4c01 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -97,7 +97,7 @@ 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; + struct net_device *dev = NULL; spin_lock_bh(&pndevs->lock); list_for_each_entry(pnd, &pndevs->list, list) { -- cgit v1.1 From 479432344420bc9a868088e346fecb6765e2b674 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 27 Jul 2009 06:15:43 +0000 Subject: net/netlabel: Correct redundant test entry was tested for NULL near the beginning of the function, followed by a return, and there is no intervening modification of its value. A simplified version of the semantic match that finds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; expression E; position p1,p2; @@ if (x == NULL || ...) { ... when forall return ...; } ... when != \(x=E\|x--\|x++\|--x\|++x\|x-=E\|x+=E\|x|=E\|x&=E\|&x\) ( *x == NULL | *x != NULL ) // Signed-off-by: Julia Lawall Acked-by: Paul Moore Signed-off-by: David S. Miller --- net/netlabel/netlabel_kapi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index b0e582f..3ff6f32 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -185,8 +185,7 @@ int netlbl_cfg_unlbl_map_add(const char *domain, return 0; cfg_unlbl_map_add_failure: - if (entry != NULL) - kfree(entry->domain); + kfree(entry->domain); kfree(entry); kfree(addrmap); kfree(map4); @@ -385,8 +384,7 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, cfg_cipsov4_map_add_failure: cipso_v4_doi_putdef(doi_def); - if (entry != NULL) - kfree(entry->domain); + kfree(entry->domain); kfree(entry); kfree(addrmap); kfree(addrinfo); -- cgit v1.1 From 8a729fce76f7af50d8b622f2fb26adce9c8df743 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 26 Jul 2009 23:18:11 +0000 Subject: net: ethtool_op_get_rx_csum() should be public and exported Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/ethtool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/ethtool.c b/net/core/ethtool.c index cf36ff4..44e5711 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -34,11 +34,13 @@ u32 ethtool_op_get_rx_csum(struct net_device *dev) { return (dev->features & NETIF_F_ALL_CSUM) != 0; } +EXPORT_SYMBOL(ethtool_op_get_rx_csum); u32 ethtool_op_get_tx_csum(struct net_device *dev) { return (dev->features & NETIF_F_ALL_CSUM) != 0; } +EXPORT_SYMBOL(ethtool_op_get_tx_csum); int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) { @@ -1125,7 +1127,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) EXPORT_SYMBOL(ethtool_op_get_link); EXPORT_SYMBOL(ethtool_op_get_sg); EXPORT_SYMBOL(ethtool_op_get_tso); -EXPORT_SYMBOL(ethtool_op_get_tx_csum); EXPORT_SYMBOL(ethtool_op_set_sg); EXPORT_SYMBOL(ethtool_op_set_tso); EXPORT_SYMBOL(ethtool_op_set_tx_csum); -- cgit v1.1 From a44a4a006b860476881ec0098c36584036e1cb91 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 27 Jul 2009 08:22:46 +0000 Subject: xfrm: export xfrm garbage collector thresholds via sysctl Export garbage collector thresholds for xfrm[4|6]_dst_ops Had a problem reported to me recently in which a high volume of ipsec connections on a system began reporting ENOBUFS for new connections eventually. It seemed that after about 2000 connections we started being unable to create more. A quick look revealed that the xfrm code used a dst_ops structure that limited the gc_thresh value to 1024, and always dropped route cache entries after 2x the gc_thresh. It seems the most direct solution is to export the gc_thresh values in the xfrm[4|6] dst_ops as sysctls, like the main routing table does, so that higher volumes of connections can be supported. This patch has been tested and allows the reporter to increase their ipsec connection volume successfully. Reported-by: Joe Nall Signed-off-by: Neil Horman ipv4/xfrm4_policy.c | 18 ++++++++++++++++++ ipv6/xfrm6_policy.c | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) Signed-off-by: David S. Miller --- net/ipv4/xfrm4_policy.c | 18 ++++++++++++++++++ net/ipv6/xfrm6_policy.c | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'net') diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 0071ee6..26496ba 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -264,6 +264,20 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { .fill_dst = xfrm4_fill_dst, }; +static struct ctl_table xfrm4_policy_table[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "xfrm4_gc_thresh", + .data = &xfrm4_dst_ops.gc_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { } +}; + +static struct ctl_table_header *sysctl_hdr; + static void __init xfrm4_policy_init(void) { xfrm_policy_register_afinfo(&xfrm4_policy_afinfo); @@ -271,6 +285,8 @@ static void __init xfrm4_policy_init(void) static void __exit xfrm4_policy_fini(void) { + if (sysctl_hdr) + unregister_net_sysctl_table(sysctl_hdr); xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); } @@ -278,5 +294,7 @@ void __init xfrm4_init(void) { xfrm4_state_init(); xfrm4_policy_init(); + sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv4_ctl_path, + xfrm4_policy_table); } diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 3a3c677..4acc308 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -306,6 +306,20 @@ static void xfrm6_policy_fini(void) xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); } +static struct ctl_table xfrm6_policy_table[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "xfrm6_gc_thresh", + .data = &xfrm6_dst_ops.gc_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { } +}; + +static struct ctl_table_header *sysctl_hdr; + int __init xfrm6_init(void) { int ret; @@ -317,6 +331,8 @@ int __init xfrm6_init(void) ret = xfrm6_state_init(); if (ret) goto out_policy; + sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv6_ctl_path, + xfrm6_policy_table); out: return ret; out_policy: @@ -326,6 +342,8 @@ out_policy: void xfrm6_fini(void) { + if (sysctl_hdr) + unregister_net_sysctl_table(sysctl_hdr); //xfrm6_input_fini(); xfrm6_policy_fini(); xfrm6_state_fini(); -- cgit v1.1 From 5061b0c2b9066de426fbc63f1278d2210e789412 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2009 00:33:34 +0200 Subject: mac80211: cooperate more with network namespaces There are still two places in mac80211 that hardcode the initial net namespace (init_net). One of them is mandated by cfg80211 and will be removed by a separate patch, the other one is used for finding the network device of a pending packet via its ifindex. Remove the latter use by keeping track of the device pointer itself, via the vif pointer, and avoid it going stale by dropping pending frames for a given interface when the interface is removed. To keep track of the vif pointer for the correct interface, change the info->control.vif pointer's internal use to always be the correct vif, and only move it to the vif the driver expects (or NULL for monitor interfaces and injected packets) right before giving the packet to the driver. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 16 ++++++++++- net/mac80211/rx.c | 2 +- net/mac80211/tx.c | 75 +++++++++++++++++++++------------------------------- 3 files changed, 46 insertions(+), 47 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2f797a8..559d698 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -335,7 +335,10 @@ static int ieee80211_stop(struct net_device *dev) struct ieee80211_local *local = sdata->local; struct ieee80211_if_init_conf conf; struct sta_info *sta; + unsigned long flags; + struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; + int i; /* * Stop TX on this interface first. @@ -551,6 +554,18 @@ static int ieee80211_stop(struct net_device *dev) if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { + skb_queue_walk_safe(&local->pending[i], skb, tmp) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + if (info->control.vif == &sdata->vif) { + __skb_unlink(skb, &local->pending[i]); + dev_kfree_skb_irq(skb); + } + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + return 0; } @@ -788,7 +803,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); - ndev->features |= NETIF_F_NETNS_LOCAL; /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ sdata = netdev_priv(ndev); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 66c797c..d9df819 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1539,7 +1539,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) info = IEEE80211_SKB_CB(fwd_skb); memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - fwd_skb->iif = rx->dev->ifindex; + info->control.vif = &rx->sdata->vif; ieee80211_select_queue(local, fwd_skb); if (is_multicast_ether_addr(fwd_hdr->addr3)) memcpy(fwd_hdr->addr1, fwd_hdr->addr3, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2572509..ffd3b10 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -400,6 +400,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) sta_info_set_tim_bit(sta); info->control.jiffies = jiffies; + info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; skb_queue_tail(&sta->ps_tx_buf, tx->skb); return TX_QUEUED; @@ -696,7 +697,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) * number, if we have no matching interface then we * neither assign one ourselves nor ask the driver to. */ - if (unlikely(!info->control.vif)) + if (unlikely(info->control.vif->type == NL80211_IFTYPE_MONITOR)) return TX_CONTINUE; if (unlikely(ieee80211_is_ctl(hdr->frame_control))) @@ -1092,6 +1093,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, } else if (*state != HT_AGG_STATE_IDLE) { /* in progress */ queued = true; + info->control.vif = &sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; __skb_queue_tail(&tid_tx->pending, skb); } @@ -1143,6 +1145,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; + struct ieee80211_sub_if_data *sdata; unsigned long flags; int ret, len; bool fragm = false; @@ -1167,7 +1170,24 @@ static int __ieee80211_tx(struct ieee80211_local *local, next = skb->next; len = skb->len; + + sdata = vif_to_sdata(info->control.vif); + + switch (sdata->vif.type) { + case NL80211_IFTYPE_MONITOR: + info->control.vif = NULL; + break; + case NL80211_IFTYPE_AP_VLAN: + info->control.vif = &container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap)->vif; + break; + default: + /* keep */ + break; + } + ret = drv_tx(local, skb); + info->control.vif = &sdata->vif; if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { dev_kfree_skb(skb); ret = NETDEV_TX_OK; @@ -1386,11 +1406,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *tmp_sdata; int headroom; bool may_encrypt; - enum { - NOT_MONITOR, - FOUND_SDATA, - UNKNOWN_ADDRESS, - } monitor_iface = NOT_MONITOR; dev_hold(sdata->dev); @@ -1424,7 +1439,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, u16 len_rthdr; info->flags |= IEEE80211_TX_CTL_INJECTED; - monitor_iface = UNKNOWN_ADDRESS; len_rthdr = ieee80211_get_radiotap_len(skb->data); hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); @@ -1454,7 +1468,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, dev_hold(tmp_sdata->dev); dev_put(sdata->dev); sdata = tmp_sdata; - monitor_iface = FOUND_SDATA; break; } } @@ -1476,13 +1489,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, return; } - tmp_sdata = sdata; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - tmp_sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); - if (likely(monitor_iface != UNKNOWN_ADDRESS)) - info->control.vif = &tmp_sdata->vif; + info->control.vif = &sdata->vif; ieee80211_select_queue(local, skb); ieee80211_tx(sdata, skb, false); @@ -1534,9 +1541,6 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, if (unlikely(skb->len < len_rthdr)) goto fail; /* skb too short for claimed rt header extent */ - /* needed because we set skb device to master */ - skb->iif = dev->ifindex; - /* * fix up the pointers accounting for the radiotap * header still being in there. We are being given @@ -1810,8 +1814,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, nh_pos += hdrlen; h_pos += hdrlen; - skb->iif = dev->ifindex; - dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; @@ -1856,32 +1858,13 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, 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; - } - - 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; - } + sdata = vif_to_sdata(info->control.vif); if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { - /* do not use sdata, it may have been changed above */ - ieee80211_tx(IEEE80211_DEV_TO_SUB_IF(dev), skb, true); + ieee80211_tx(sdata, skb, true); } else { hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(local, hdr->addr1); @@ -1891,9 +1874,6 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, result = false; } - out: - dev_put(dev); - return result; } @@ -1921,10 +1901,16 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = __skb_dequeue(&local->pending[i]); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sub_if_data *sdata; + + sdata = vif_to_sdata(info->control.vif); + dev_hold(sdata->dev); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); txok = ieee80211_tx_pending_skb(local, skb); + dev_put(sdata->dev); if (!txok) __skb_queue_head(&local->pending[i], skb); spin_lock_irqsave(&local->queue_stop_reason_lock, @@ -2234,7 +2220,6 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); - skb->iif = sdata->dev->ifindex; if (!encrypt) info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -- cgit v1.1 From 463d018323851a608eef52a9427b0585005c647f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2009 00:33:35 +0200 Subject: cfg80211: make aware of net namespaces In order to make cfg80211/nl80211 aware of network namespaces, we have to do the following things: * del_virtual_intf method takes an interface index rather than a netdev pointer - simply change this * nl80211 uses init_net a lot, it changes to use the sender's network namespace * scan requests use the interface index, hold a netdev pointer and reference instead * we want a wiphy and its associated virtual interfaces to be in one netns together, so - we need to be able to change ns for a given interface, so export dev_change_net_namespace() - for each virtual interface set the NETIF_F_NETNS_LOCAL flag, and clear that flag only when the wiphy changes ns, to disallow breaking this invariant * when a network namespace goes away, we need to reparent the wiphy to init_net * cfg80211 users that support creating virtual interfaces must create them in the wiphy's namespace, currently this affects only mac80211 The end result is that you can now switch an entire wiphy into a different network namespace with the new command iw phy# set netns and all virtual interfaces will follow (or the operation fails). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/core/dev.c | 1 + net/mac80211/cfg.c | 14 +--- net/wireless/core.c | 75 ++++++++++++++++-- net/wireless/core.h | 5 +- net/wireless/nl80211.c | 202 ++++++++++++++++++++++++++++++++++--------------- net/wireless/scan.c | 22 ++---- net/wireless/sme.c | 3 +- 7 files changed, 227 insertions(+), 95 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index d6c657e..7134766 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5344,6 +5344,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char out: return err; } +EXPORT_SYMBOL_GPL(dev_change_net_namespace); static int dev_cpu_callback(struct notifier_block *nfb, unsigned long action, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 52928ad..4bbf500 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -57,19 +57,9 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name, return 0; } -static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) +static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev) { - struct net_device *dev; - struct ieee80211_sub_if_data *sdata; - - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - ieee80211_if_remove(sdata); + ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(dev)); return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 6891cd0..442c9f3 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -106,7 +106,7 @@ __cfg80211_rdev_from_info(struct genl_info *info) if (info->attrs[NL80211_ATTR_IFINDEX]) { ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(genl_info_net(info), ifindex); if (dev) { if (dev->ieee80211_ptr) byifidx = @@ -151,13 +151,13 @@ cfg80211_get_dev_from_info(struct genl_info *info) } struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(int ifindex) +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) { struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(net, ifindex); if (!dev) goto out; if (dev->ieee80211_ptr) { @@ -222,6 +222,42 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; } +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net) +{ + struct wireless_dev *wdev; + int err = 0; + + if (!rdev->wiphy.netnsok) + return -EOPNOTSUPP; + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + if (err) + break; + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + + if (err) { + /* failed -- clean up to old netns */ + net = wiphy_net(&rdev->wiphy); + + list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, + list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, + "wlan%d"); + WARN_ON(err); + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + } + + wiphy_net_set(&rdev->wiphy, net); + + return err; +} + static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { struct cfg80211_registered_device *rdev = data; @@ -375,6 +411,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + wiphy_net_set(&rdev->wiphy, &init_net); + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), &rdev->wiphy.dev, RFKILL_TYPE_WLAN, @@ -615,6 +653,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); + /* can only change netns with wiphy */ + dev->features |= NETIF_F_NETNS_LOCAL; + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { printk(KERN_ERR "wireless: failed to add phy80211 " @@ -705,10 +746,32 @@ static struct notifier_block cfg80211_netdev_notifier = { .notifier_call = cfg80211_netdev_notifier_call, }; -static int cfg80211_init(void) +static void __net_exit cfg80211_pernet_exit(struct net *net) +{ + struct cfg80211_registered_device *rdev; + + rtnl_lock(); + mutex_lock(&cfg80211_mutex); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (net_eq(wiphy_net(&rdev->wiphy), net)) + WARN_ON(cfg80211_switch_netns(rdev, &init_net)); + } + mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); +} + +static struct pernet_operations cfg80211_pernet_ops = { + .exit = cfg80211_pernet_exit, +}; + +static int __init cfg80211_init(void) { int err; + err = register_pernet_device(&cfg80211_pernet_ops); + if (err) + goto out_fail_pernet; + err = wiphy_sysfs_init(); if (err) goto out_fail_sysfs; @@ -736,9 +799,10 @@ out_fail_nl80211: out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: + unregister_pernet_device(&cfg80211_pernet_ops); +out_fail_pernet: return err; } - subsys_initcall(cfg80211_init); static void cfg80211_exit(void) @@ -748,5 +812,6 @@ static void cfg80211_exit(void) unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); regulatory_exit(); + unregister_pernet_device(&cfg80211_pernet_ops); } module_exit(cfg80211_exit); diff --git a/net/wireless/core.h b/net/wireless/core.h index 2ec8ddb..4276b70 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -170,7 +170,10 @@ 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); +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); + +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net); static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index da450ef..7880a9c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include "core.h" #include "nl80211.h" #include "reg.h" @@ -27,24 +29,26 @@ static struct genl_family nl80211_fam = { .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, + .netnsok = true, }; /* internal helper: get rdev and dev */ -static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, +static int get_rdev_dev_by_info_ifindex(struct genl_info *info, struct cfg80211_registered_device **rdev, struct net_device **dev) { + struct nlattr **attrs = info->attrs; int ifindex; if (!attrs[NL80211_ATTR_IFINDEX]) return -EINVAL; ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(&init_net, ifindex); + *dev = dev_get_by_index(genl_info_net(info), ifindex); if (!*dev) return -ENODEV; - *rdev = cfg80211_get_dev_from_ifindex(ifindex); + *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); if (IS_ERR(*rdev)) { dev_put(*dev); return PTR_ERR(*rdev); @@ -133,6 +137,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + [NL80211_ATTR_PID] = { .type = NLA_U32 }, }; /* policy for the attributes */ @@ -532,6 +537,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); + if (dev->wiphy.netnsok) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); + } #undef CMD @@ -562,6 +571,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -867,6 +878,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (wp_idx < wp_start) { wp_idx++; continue; @@ -907,7 +920,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev); if (err) return err; @@ -975,7 +988,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1098,26 +1111,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int ifindex, err; + int err; struct net_device *dev; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; - ifindex = dev->ifindex; - dev_put(dev); if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev); out: cfg80211_unlock_rdev(rdev); + dev_put(dev); unlock_rtnl: rtnl_unlock(); return err; @@ -1195,7 +1207,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1274,7 +1286,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1333,7 +1345,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1380,7 +1392,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1429,7 +1441,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1516,7 +1528,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1726,13 +1738,13 @@ static int nl80211_dump_station(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -1791,7 +1803,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -1829,14 +1841,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) /* * Get vlan interface making sure it is on the right wiphy. */ -static int get_vlan(struct nlattr *vlanattr, +static int get_vlan(struct genl_info *info, struct cfg80211_registered_device *rdev, struct net_device **vlan) { + struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; *vlan = NULL; if (vlanattr) { - *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr)); + *vlan = dev_get_by_index(genl_info_net(info), + nla_get_u32(vlanattr)); if (!*vlan) return -ENODEV; if (!(*vlan)->ieee80211_ptr) @@ -1891,11 +1905,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2004,11 +2018,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2079,7 +2093,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2185,13 +2199,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -2255,7 +2269,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2314,7 +2328,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2362,7 +2376,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2404,7 +2418,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2455,7 +2469,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2574,7 +2588,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2691,7 +2705,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2947,7 +2961,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -3069,14 +3083,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->ie_len); } - request->ifidx = dev->ifindex; + request->dev = dev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; err = rdev->ops->scan(&rdev->wiphy, dev, request); - if (!err) + if (!err) { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out_free: if (err) { @@ -3198,11 +3214,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[0] = ifidx; } - dev = dev_get_by_index(&init_net, ifidx); + dev = dev_get_by_index(sock_net(skb->sk), ifidx); if (!dev) return -ENODEV; - rdev = cfg80211_get_dev_from_ifindex(ifidx); + rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); goto out_put_netdev; @@ -3312,7 +3328,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3448,7 +3464,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3531,7 +3547,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3593,7 +3609,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3666,7 +3682,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3739,7 +3755,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3924,7 +3940,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4000,7 +4016,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4024,6 +4040,47 @@ unlock_rtnl: return err; } +static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net *net; + int err; + u32 pid; + + if (!info->attrs[NL80211_ATTR_PID]) + return -EINVAL; + + pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); + + rtnl_lock(); + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + goto out; + } + + net = get_net_ns_by_pid(pid); + if (IS_ERR(net)) { + err = PTR_ERR(net); + goto out; + } + + err = 0; + + /* check if anything to do */ + if (net_eq(wiphy_net(&rdev->wiphy), net)) + goto out_put_net; + + err = cfg80211_switch_netns(rdev, net); + out_put_net: + put_net(net); + out: + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -4257,6 +4314,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_WIPHY_NETNS, + .doit = nl80211_wiphy_netns, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4288,7 +4351,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) return; } - genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_config_mcgrp.id, GFP_KERNEL); } static int nl80211_add_scan_req(struct sk_buff *msg, @@ -4365,7 +4429,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, @@ -4383,7 +4448,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, @@ -4401,7 +4467,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } /* @@ -4450,7 +4517,10 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL); + rtnl_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_KERNEL); + rtnl_unlock(); return; @@ -4486,7 +4556,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4553,7 +4624,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4611,7 +4683,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4651,7 +4724,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4691,7 +4765,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: @@ -4726,7 +4801,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4766,7 +4842,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4819,7 +4896,10 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC); + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_ATOMIC); + rcu_read_unlock(); return; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index decc59f..1b578b8 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -32,9 +32,7 @@ void __cfg80211_scan_done(struct work_struct *wk) mutex_lock(&rdev->mtx); request = rdev->scan_req; - dev = dev_get_by_index(&init_net, request->ifidx); - if (!dev) - goto out; + dev = request->dev; /* * This must be before sending the other events! @@ -58,7 +56,6 @@ void __cfg80211_scan_done(struct work_struct *wk) dev_put(dev); - out: cfg80211_unlock_rdev(rdev); wiphy_to_dev(request->wiphy)->scan_req = NULL; kfree(request); @@ -66,17 +63,10 @@ void __cfg80211_scan_done(struct work_struct *wk) void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { - struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); - if (WARN_ON(!dev)) { - kfree(request); - return; - } - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); - dev_put(dev); } EXPORT_SYMBOL(cfg80211_scan_done); @@ -592,7 +582,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -617,7 +607,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, } creq->wiphy = wiphy; - creq->ifidx = dev->ifindex; + creq->dev = dev; creq->ssids = (void *)(creq + 1); creq->channels = (void *)(creq->ssids + 1); creq->n_channels = n_channels; @@ -654,8 +644,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (err) { rdev->scan_req = NULL; kfree(creq); - } else + } else { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out: cfg80211_unlock_rdev(rdev); return err; @@ -948,7 +940,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 82de2d9..a197410 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -86,7 +86,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) wdev->conn->params.ssid_len); request->ssids[0].ssid_len = wdev->conn->params.ssid_len; - request->ifidx = wdev->netdev->ifindex; + request->dev = wdev->netdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; @@ -95,6 +95,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; nl80211_send_scan_start(rdev, wdev->netdev); + dev_hold(wdev->netdev); } else { rdev->scan_req = NULL; kfree(request); -- cgit v1.1 From a272a720660059c30fa038113b77fa2a096437d9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Jul 2009 00:33:36 +0200 Subject: mac80211: allow using network namespaces This finally opens up the ability to put mac80211 devices into different network namespaces. As long as you don't have sysfs, that is. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 1 + net/mac80211/main.c | 1 + 2 files changed, 2 insertions(+) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 559d698..0cb29df 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -787,6 +787,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, name, ieee80211_if_setup); if (!ndev) return -ENOMEM; + dev_net_set(ndev, wiphy_net(local->hw.wiphy)); ndev->needed_headroom = local->tx_headroom + 4*6 /* four MAC addresses */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 3234f37..02cabbf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -620,6 +620,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, if (!wiphy) return NULL; + wiphy->netnsok = true; wiphy->privid = mac80211_wiphy_privid; /* Yes, putting cfg80211_bss into ieee80211_bss is a hack */ -- cgit v1.1 From 7d3be3cc489176bc7bd23e673b0b4aef597af2b3 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:13:41 +0200 Subject: mac80211: refactor the scan code Move the processing of each scan state into its own functions for better readability. This patch does not introduce functional changes. Signed-off-by: Helmut Schaa Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/scan.c | 136 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 58 deletions(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7482065..71500f1 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -474,13 +474,87 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, return rc; } +static int ieee80211_scan_state_set_channel(struct ieee80211_local *local, + unsigned long *next_delay) +{ + int skip; + struct ieee80211_channel *chan; + struct ieee80211_sub_if_data *sdata = local->scan_sdata; + + /* if no more bands/channels left, complete scan */ + if (local->scan_channel_idx >= local->scan_req->n_channels) { + ieee80211_scan_completed(&local->hw, false); + return 1; + } + skip = 0; + chan = local->scan_req->channels[local->scan_channel_idx]; + + if (chan->flags & IEEE80211_CHAN_DISABLED || + (sdata->vif.type == NL80211_IFTYPE_ADHOC && + chan->flags & IEEE80211_CHAN_NO_IBSS)) + skip = 1; + + if (!skip) { + local->scan_channel = chan; + if (ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_CHANNEL)) + skip = 1; + } + + /* advance state machine to next channel/band */ + local->scan_channel_idx++; + + if (skip) + return 0; + + /* + * Probe delay is used to update the NAV, cf. 11.1.3.2.2 + * (which unfortunately doesn't say _why_ step a) is done, + * but it waits for the probe delay or until a frame is + * received - and the received frame would update the NAV). + * For now, we do not support waiting until a frame is + * received. + * + * In any case, it is not necessary for a passive scan. + */ + if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || + !local->scan_req->n_ssids) { + *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; + return 0; + } + + *next_delay = IEEE80211_PROBE_DELAY; + local->scan_state = SCAN_SEND_PROBE; + + return 0; +} + +static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, + unsigned long *next_delay) +{ + int i; + struct ieee80211_sub_if_data *sdata = local->scan_sdata; + + 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, + local->scan_req->ie, local->scan_req->ie_len); + + /* + * After sending probe requests, wait for probe responses + * on the channel. + */ + *next_delay = IEEE80211_CHANNEL_TIME; + local->scan_state = SCAN_SET_CHANNEL; +} + 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_channel *chan; - int skip, i; unsigned long next_delay = 0; mutex_lock(&local->scan_mtx); @@ -515,65 +589,11 @@ void ieee80211_scan_work(struct work_struct *work) switch (local->scan_state) { case SCAN_SET_CHANNEL: - /* if no more bands/channels left, complete scan */ - if (local->scan_channel_idx >= local->scan_req->n_channels) { - ieee80211_scan_completed(&local->hw, false); + if (ieee80211_scan_state_set_channel(local, &next_delay)) return; - } - skip = 0; - chan = local->scan_req->channels[local->scan_channel_idx]; - - if (chan->flags & IEEE80211_CHAN_DISABLED || - (sdata->vif.type == NL80211_IFTYPE_ADHOC && - chan->flags & IEEE80211_CHAN_NO_IBSS)) - skip = 1; - - if (!skip) { - local->scan_channel = chan; - if (ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_CHANNEL)) - skip = 1; - } - - /* advance state machine to next channel/band */ - local->scan_channel_idx++; - - if (skip) - break; - - /* - * Probe delay is used to update the NAV, cf. 11.1.3.2.2 - * (which unfortunately doesn't say _why_ step a) is done, - * but it waits for the probe delay or until a frame is - * received - and the received frame would update the NAV). - * For now, we do not support waiting until a frame is - * received. - * - * In any case, it is not necessary for a passive scan. - */ - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || - !local->scan_req->n_ssids) { - next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; - break; - } - - next_delay = IEEE80211_PROBE_DELAY; - local->scan_state = SCAN_SEND_PROBE; break; case SCAN_SEND_PROBE: - 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, - local->scan_req->ie, local->scan_req->ie_len); - - /* - * After sending probe requests, wait for probe responses - * on the channel. - */ - next_delay = IEEE80211_CHANNEL_TIME; - local->scan_state = SCAN_SET_CHANNEL; + ieee80211_scan_state_send_probe(local, &next_delay); break; } -- cgit v1.1 From f502d09b750437a4ec9c63333acf1070fe7958af Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:13:48 +0200 Subject: mac80211: advance the state machine immediately if no delay is needed Instead of queueing the scan work again without delay just process the next state immediately. Signed-off-by: Helmut Schaa Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/scan.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 71500f1..db122e4 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -587,15 +587,21 @@ void ieee80211_scan_work(struct work_struct *work) return; } - switch (local->scan_state) { - case SCAN_SET_CHANNEL: - if (ieee80211_scan_state_set_channel(local, &next_delay)) - return; - break; - case SCAN_SEND_PROBE: - ieee80211_scan_state_send_probe(local, &next_delay); - break; - } + /* + * as long as no delay is required advance immediately + * without scheduling a new work + */ + do { + switch (local->scan_state) { + case SCAN_SET_CHANNEL: + if (ieee80211_scan_state_set_channel(local, &next_delay)) + return; + break; + case SCAN_SEND_PROBE: + ieee80211_scan_state_send_probe(local, &next_delay); + break; + } + } while (next_delay == 0); queue_delayed_work(local->hw.workqueue, &local->scan_work, next_delay); -- cgit v1.1 From 2fb3f028a9a46bd344329766257699b4acb36525 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:13:56 +0200 Subject: mac80211: introduce a new scan state "decision" Introduce a new scan state "decision" which is entered after every completed scan operation and decides about the next steps. At first the decision is in any case to scan the next channel. This shouldn't introduce any functional changes. Signed-off-by: Helmut Schaa Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/scan.c | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6a01771..4166418 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -678,7 +678,7 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; - enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; + enum { SCAN_DECISION, SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; enum nl80211_channel_type oper_channel_type; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index db122e4..48f910a 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -376,7 +376,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) } mutex_unlock(&local->iflist_mtx); - local->scan_state = SCAN_SET_CHANNEL; + local->scan_state = SCAN_DECISION; local->scan_channel_idx = 0; spin_lock_bh(&local->filter_lock); @@ -474,18 +474,27 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, return rc; } -static int ieee80211_scan_state_set_channel(struct ieee80211_local *local, - unsigned long *next_delay) +static int ieee80211_scan_state_decision(struct ieee80211_local *local, + unsigned long *next_delay) { - int skip; - struct ieee80211_channel *chan; - struct ieee80211_sub_if_data *sdata = local->scan_sdata; - /* if no more bands/channels left, complete scan */ if (local->scan_channel_idx >= local->scan_req->n_channels) { ieee80211_scan_completed(&local->hw, false); return 1; } + + *next_delay = 0; + local->scan_state = SCAN_SET_CHANNEL; + return 0; +} + +static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, + unsigned long *next_delay) +{ + int skip; + struct ieee80211_channel *chan; + struct ieee80211_sub_if_data *sdata = local->scan_sdata; + skip = 0; chan = local->scan_req->channels[local->scan_channel_idx]; @@ -505,7 +514,7 @@ static int ieee80211_scan_state_set_channel(struct ieee80211_local *local, local->scan_channel_idx++; if (skip) - return 0; + return; /* * Probe delay is used to update the NAV, cf. 11.1.3.2.2 @@ -520,13 +529,13 @@ static int ieee80211_scan_state_set_channel(struct ieee80211_local *local, if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || !local->scan_req->n_ssids) { *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; - return 0; + local->scan_state = SCAN_DECISION; + return; } + /* active scan, send probes */ *next_delay = IEEE80211_PROBE_DELAY; local->scan_state = SCAN_SEND_PROBE; - - return 0; } static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, @@ -547,7 +556,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, * on the channel. */ *next_delay = IEEE80211_CHANNEL_TIME; - local->scan_state = SCAN_SET_CHANNEL; + local->scan_state = SCAN_DECISION; } void ieee80211_scan_work(struct work_struct *work) @@ -593,10 +602,13 @@ void ieee80211_scan_work(struct work_struct *work) */ do { switch (local->scan_state) { - case SCAN_SET_CHANNEL: - if (ieee80211_scan_state_set_channel(local, &next_delay)) + case SCAN_DECISION: + if (ieee80211_scan_state_decision(local, &next_delay)) return; break; + case SCAN_SET_CHANNEL: + ieee80211_scan_state_set_channel(local, &next_delay); + break; case SCAN_SEND_PROBE: ieee80211_scan_state_send_probe(local, &next_delay); break; -- cgit v1.1 From fbe9c429f195111bbf7f1630efa19aee295fd8e7 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:14:04 +0200 Subject: mac80211: Replace {sw, hw}_scanning variables with a bitfield Use a bitfield to store the current scan mode instead of two boolean variables {sw,hw}_scanning. This patch does not introduce functional changes but allows us to enhance the scan flags later (for example for background scanning). Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 7 ++++++- net/mac80211/iface.c | 4 ++-- net/mac80211/main.c | 2 +- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 8 ++++---- net/mac80211/rx.c | 6 +++--- net/mac80211/scan.c | 23 ++++++++++------------- net/mac80211/tx.c | 6 +++--- 9 files changed, 31 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8e22200..6e3cca6 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -742,7 +742,7 @@ static void ieee80211_ibss_work(struct work_struct *work) if (!netif_running(sdata->dev)) return; - if (local->sw_scanning || local->hw_scanning) + if (local->scanning) return; if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_ADHOC)) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4166418..783a125 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -570,6 +570,11 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_SKB_ADD, }; +enum { + SCAN_SW_SCANNING, + SCAN_HW_SCANNING +}; + struct ieee80211_local { /* embed the driver visible part. * don't cast (use the static inlines below), but we keep @@ -668,7 +673,7 @@ struct ieee80211_local { /* Scanning and BSS list */ struct mutex scan_mtx; - bool sw_scanning, hw_scanning; + unsigned long scanning; struct cfg80211_ssid scan_ssid; struct cfg80211_scan_request int_scan_req; struct cfg80211_scan_request *scan_req; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0cb29df..d79a211 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -518,7 +518,7 @@ static int ieee80211_stop(struct net_device *dev) * the scan_sdata is NULL already don't send out a * scan event to userspace -- the scan is incomplete. */ - if (local->sw_scanning) + if (test_bit(SCAN_SW_SCANNING, &local->scanning)) ieee80211_scan_completed(&local->hw, true); } @@ -920,7 +920,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; int count = 0; - if (local->hw_scanning || local->sw_scanning) + if (local->scanning) return ieee80211_idle_off(local, "scanning"); list_for_each_entry(sdata, &local->interfaces, list) { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 02cabbf..c1a7991 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -198,7 +198,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, } if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (local->sw_scanning) { + if (test_bit(SCAN_SW_SCANNING, &local->scanning)) { sdata->vif.bss_conf.enable_beacon = false; } else { /* diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8a97b14..9a38269 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -597,7 +597,7 @@ static void ieee80211_mesh_work(struct work_struct *work) if (!netif_running(sdata->dev)) return; - if (local->sw_scanning || local->hw_scanning) + if (local->scanning) return; while ((skb = skb_dequeue(&ifmsh->skb_queue))) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 523c0d9..52b6f83 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -581,7 +581,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (!ifmgd->associated) return; - if (sdata->local->sw_scanning || sdata->local->hw_scanning) + if (sdata->local->scanning) return; /* Disregard subsequent beacons if we are already running a timer @@ -639,7 +639,7 @@ static void ieee80211_enable_ps(struct ieee80211_local *local, * If we are scanning right now then the parameters will * take effect when scan finishes. */ - if (local->hw_scanning || local->sw_scanning) + if (local->scanning) return; if (conf->dynamic_ps_timeout > 0 && @@ -2038,7 +2038,7 @@ static void ieee80211_sta_work(struct work_struct *work) if (!netif_running(sdata->dev)) return; - if (local->sw_scanning || local->hw_scanning) + if (local->scanning) return; if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) @@ -2213,7 +2213,7 @@ static void ieee80211_sta_monitor_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.monitor_work); - if (sdata->local->sw_scanning || sdata->local->hw_scanning) + if (sdata->local->scanning) return; ieee80211_mgd_probe_ap(sdata, false); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d9df819..9c1679d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -418,10 +418,10 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct sk_buff *skb = rx->skb; - if (unlikely(local->hw_scanning)) + if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning))) return ieee80211_scan_rx(rx->sdata, skb); - if (unlikely(local->sw_scanning)) { + if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning))) { /* drop all the other packets during a software scan anyway */ if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) dev_kfree_skb(skb); @@ -2136,7 +2136,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, return; } - if (unlikely(local->sw_scanning || local->hw_scanning)) + if (unlikely(local->scanning)) rx.flags |= IEEE80211_RX_IN_SCAN; ieee80211_parse_qos(&rx); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 48f910a..4233c3d 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -265,7 +265,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) mutex_lock(&local->scan_mtx); - if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) { + if (WARN_ON(!local->scanning)) { mutex_unlock(&local->scan_mtx); return; } @@ -275,16 +275,15 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) return; } - if (local->hw_scanning) + if (test_bit(SCAN_HW_SCANNING, &local->scanning)) ieee80211_restore_scan_ies(local); if (local->scan_req != &local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; - was_hw_scan = local->hw_scanning; - local->hw_scanning = false; - local->sw_scanning = false; + was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); + local->scanning = 0; local->scan_channel = NULL; /* we only have to protect scan_req and hw/sw scan */ @@ -434,9 +433,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, } if (local->ops->hw_scan) - local->hw_scanning = true; + __set_bit(SCAN_HW_SCANNING, &local->scanning); else - local->sw_scanning = true; + __set_bit(SCAN_SW_SCANNING, &local->scanning); /* * Kicking off the scan need not be protected, * only the scan variable stuff, since now @@ -459,11 +458,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->scan_mtx); if (rc) { - if (local->ops->hw_scan) { - local->hw_scanning = false; + if (local->ops->hw_scan) ieee80211_restore_scan_ies(local); - } else - local->sw_scanning = false; + local->scanning = 0; ieee80211_recalc_idle(local); @@ -572,7 +569,7 @@ void ieee80211_scan_work(struct work_struct *work) return; } - if (local->scan_req && !(local->sw_scanning || local->hw_scanning)) { + if (local->scan_req && !local->scanning) { struct cfg80211_scan_request *req = local->scan_req; int rc; @@ -663,7 +660,7 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * queued -- mostly at suspend under RTNL. */ mutex_lock(&local->scan_mtx); - swscan = local->sw_scanning; + swscan = test_bit(SCAN_SW_SCANNING, &local->scanning); mutex_unlock(&local->scan_mtx); if (swscan) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ffd3b10..d7491dc 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -192,7 +192,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; - if (unlikely(tx->local->sw_scanning) && + if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control)) /* @@ -552,7 +552,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) * Lets not bother rate control if we're associated and cannot * talk to the sta. This should not happen. */ - if (WARN((tx->local->sw_scanning) && + if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && (sta_flags & WLAN_STA_ASSOC) && !rate_usable_index_exists(sband, &tx->sta->sta), "%s: Dropped data frame as no usable bitrate found while " @@ -1411,7 +1411,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && local->hw.conf.dynamic_ps_timeout > 0 && - !local->sw_scanning && !local->hw_scanning && local->ps_sdata) { + !(local->scanning) && local->ps_sdata) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); -- cgit v1.1 From 142b9f5074dc0d09dc0025739ad437723d7bf527 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 13:18:01 +0200 Subject: mac80211: implement basic background scanning Introduce a new scan flag "SCAN_OFF_CHANNEL" which basically tells us that we are currently on a different channel for scanning and cannot RX/TX. "SCAN_SW_SCANNING" tells us that we are currently running a software scan but we might as well be on the operating channel to RX/TX. While "SCAN_SW_SCANNING" is set during the whole scan "SCAN_OFF_CHANNEL" is set when leaving the operating channel and unset when coming back. Introduce two new scan states "SCAN_LEAVE_OPER_CHANNEL" and "SCAN_ENTER_OPER_CHANNEL" which basically implement the functionality we need to leave the operating channel (send a nullfunc to the AP and stop the queues) and enter it again (send a nullfunc to the AP and start the queues again). Enhance the scan state "SCAN_DECISION" to switch back to the operating channel after each scanned channel. In the future it sould be simple to enhance the decision state to scan as much channels in a row as the qos latency allows us. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 36 +++++++++++++- net/mac80211/rx.c | 6 ++- net/mac80211/scan.c | 117 +++++++++++++++++++++++++++++++++++++++++---- net/mac80211/tx.c | 2 +- 4 files changed, 148 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 783a125..efda19e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -570,9 +570,41 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_SKB_ADD, }; +/** + * mac80211 scan flags - currently active scan mode + * + * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as + * well be on the operating channel + * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to + * determine if we are on the operating channel or not + * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning, + * gets only set in conjunction with SCAN_SW_SCANNING + */ enum { SCAN_SW_SCANNING, - SCAN_HW_SCANNING + SCAN_HW_SCANNING, + SCAN_OFF_CHANNEL, +}; + +/** + * enum mac80211_scan_state - scan state machine states + * + * @SCAN_DECISION: Main entry point to the scan state machine, this state + * determines if we should keep on scanning or switch back to the + * operating channel + * @SCAN_SET_CHANNEL: Set the next channel to be scanned + * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses + * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP + * about us leaving the channel and stop all associated STA interfaces + * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the + * AP about us being back and restart all associated STA interfaces + */ +enum mac80211_scan_state { + SCAN_DECISION, + SCAN_SET_CHANNEL, + SCAN_SEND_PROBE, + SCAN_LEAVE_OPER_CHANNEL, + SCAN_ENTER_OPER_CHANNEL, }; struct ieee80211_local { @@ -683,7 +715,7 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; - enum { SCAN_DECISION, SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; + enum mac80211_scan_state scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; enum nl80211_channel_type oper_channel_type; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9c1679d..cb95a31 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -421,7 +421,8 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning))) return ieee80211_scan_rx(rx->sdata, skb); - if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning))) { + if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning) && + (rx->flags & IEEE80211_RX_IN_SCAN))) { /* drop all the other packets during a software scan anyway */ if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) dev_kfree_skb(skb); @@ -2136,7 +2137,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, return; } - if (unlikely(local->scanning)) + if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || + test_bit(SCAN_OFF_CHANNEL, &local->scanning))) rx.flags |= IEEE80211_RX_IN_SCAN; ieee80211_parse_qos(&rx); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4233c3d..d56b9da 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -365,12 +365,11 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.associated) { - netif_tx_stop_all_queues(sdata->dev); - ieee80211_scan_ps_enable(sdata); - } - } else + /* + * only handle non-STA interfaces here, STA interfaces + * are handled in the scan state machine + */ + if (sdata->vif.type != NL80211_IFTYPE_STATION) netif_tx_stop_all_queues(sdata->dev); } mutex_unlock(&local->iflist_mtx); @@ -474,17 +473,113 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, static int ieee80211_scan_state_decision(struct ieee80211_local *local, unsigned long *next_delay) { - /* if no more bands/channels left, complete scan */ + bool associated = false; + struct ieee80211_sub_if_data *sdata; + + /* if no more bands/channels left, complete scan and advance to the idle state */ if (local->scan_channel_idx >= local->scan_req->n_channels) { ieee80211_scan_completed(&local->hw, false); return 1; } + /* check if at least one STA interface is associated */ + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + if (!netif_running(sdata->dev)) + continue; + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + if (sdata->u.mgd.associated) { + associated = true; + break; + } + } + } + mutex_unlock(&local->iflist_mtx); + + if (local->scan_channel) { + /* + * we're currently scanning a different channel, let's + * switch back to the operating channel now if at least + * one interface is associated. Otherwise just scan the + * next channel + */ + if (associated) + local->scan_state = SCAN_ENTER_OPER_CHANNEL; + else + local->scan_state = SCAN_SET_CHANNEL; + } else { + /* + * we're on the operating channel currently, let's + * leave that channel now to scan another one + */ + local->scan_state = SCAN_LEAVE_OPER_CHANNEL; + } + *next_delay = 0; - local->scan_state = SCAN_SET_CHANNEL; return 0; } +static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, + unsigned long *next_delay) +{ + struct ieee80211_sub_if_data *sdata; + + /* + * notify the AP about us leaving the channel and stop all STA interfaces + */ + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + if (!netif_running(sdata->dev)) + continue; + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + netif_tx_stop_all_queues(sdata->dev); + if (sdata->u.mgd.associated) + ieee80211_scan_ps_enable(sdata); + } + } + mutex_unlock(&local->iflist_mtx); + + __set_bit(SCAN_OFF_CHANNEL, &local->scanning); + + /* advance to the next channel to be scanned */ + *next_delay = HZ / 10; + local->scan_state = SCAN_SET_CHANNEL; +} + +static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, + unsigned long *next_delay) +{ + struct ieee80211_sub_if_data *sdata = local->scan_sdata; + + /* switch back to the operating channel */ + local->scan_channel = NULL; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + + /* + * notify the AP about us being back and restart all STA interfaces + */ + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + if (!netif_running(sdata->dev)) + continue; + + /* Tell AP we're back */ + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + if (sdata->u.mgd.associated) + ieee80211_scan_ps_disable(sdata); + netif_tx_wake_all_queues(sdata->dev); + } + } + mutex_unlock(&local->iflist_mtx); + + __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); + + *next_delay = HZ / 5; + local->scan_state = SCAN_DECISION; +} + static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, unsigned long *next_delay) { @@ -609,6 +704,12 @@ void ieee80211_scan_work(struct work_struct *work) case SCAN_SEND_PROBE: ieee80211_scan_state_send_probe(local, &next_delay); break; + case SCAN_LEAVE_OPER_CHANNEL: + ieee80211_scan_state_leave_oper_channel(local, &next_delay); + break; + case SCAN_ENTER_OPER_CHANNEL: + ieee80211_scan_state_enter_oper_channel(local, &next_delay); + break; } } while (next_delay == 0); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d7491dc..70ff4f0 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -192,7 +192,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; - if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && + if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) && !ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_nullfunc(hdr->frame_control)) /* -- cgit v1.1 From 977923b00c79185c11b4b47664f5ffa4c3820438 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:14:20 +0200 Subject: mac80211: rename scan_state to next_scan_state Rename scan_state to next_scan_state to better reflect what it is used for. Signed-off-by: Helmut Schaa Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/scan.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index efda19e..c6b25cb 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -715,7 +715,7 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; - enum mac80211_scan_state scan_state; + enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; enum nl80211_channel_type oper_channel_type; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d56b9da..b376775 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -374,7 +374,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) } mutex_unlock(&local->iflist_mtx); - local->scan_state = SCAN_DECISION; + local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; spin_lock_bh(&local->filter_lock); @@ -505,15 +505,15 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, * next channel */ if (associated) - local->scan_state = SCAN_ENTER_OPER_CHANNEL; + local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; else - local->scan_state = SCAN_SET_CHANNEL; + local->next_scan_state = SCAN_SET_CHANNEL; } else { /* * we're on the operating channel currently, let's * leave that channel now to scan another one */ - local->scan_state = SCAN_LEAVE_OPER_CHANNEL; + local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; } *next_delay = 0; @@ -545,7 +545,7 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca /* advance to the next channel to be scanned */ *next_delay = HZ / 10; - local->scan_state = SCAN_SET_CHANNEL; + local->next_scan_state = SCAN_SET_CHANNEL; } static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, @@ -577,7 +577,7 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); *next_delay = HZ / 5; - local->scan_state = SCAN_DECISION; + local->next_scan_state = SCAN_DECISION; } static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, @@ -621,13 +621,13 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || !local->scan_req->n_ssids) { *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; - local->scan_state = SCAN_DECISION; + local->next_scan_state = SCAN_DECISION; return; } /* active scan, send probes */ *next_delay = IEEE80211_PROBE_DELAY; - local->scan_state = SCAN_SEND_PROBE; + local->next_scan_state = SCAN_SEND_PROBE; } static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, @@ -648,7 +648,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, * on the channel. */ *next_delay = IEEE80211_CHANNEL_TIME; - local->scan_state = SCAN_DECISION; + local->next_scan_state = SCAN_DECISION; } void ieee80211_scan_work(struct work_struct *work) @@ -693,7 +693,7 @@ void ieee80211_scan_work(struct work_struct *work) * without scheduling a new work */ do { - switch (local->scan_state) { + switch (local->next_scan_state) { case SCAN_DECISION: if (ieee80211_scan_state_decision(local, &next_delay)) return; -- cgit v1.1 From 09f97e0fc4ae81f151bd76b97e28d2af429c1427 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 23 Jul 2009 12:14:29 +0200 Subject: cfg80211: increase scan result expire time Using background scanning in mac80211 the time a scan needs to finish can exceed 10 seconds. Hence, increase the scan results expire time to 15 seconds which should be sufficient. Signed-off-by: Helmut Schaa Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1b578b8..2a2683f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -15,7 +15,7 @@ #include "core.h" #include "nl80211.h" -#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) void __cfg80211_scan_done(struct work_struct *wk) { -- cgit v1.1 From 485318471e85c1ddb5e3056fa30fdbbc46d759c6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 Jul 2009 16:50:16 +0200 Subject: mac80211: fix mlme timeouts When a new MLME work is created, its timeout is initialised to 0. This is wrong, it could then be thought of as having an actual timeout in the future (time_is_after_jiffies() can return true). Instead, it should be initialised to jiffies so that it will run right away as soon as the mlme work is executed. Signed-off-by: Johannes Berg Reported-by: Luciano Roth Coelho Reported-by: Alban Browaeys Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 52b6f83..807ab89 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2377,6 +2377,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, wk->state = IEEE80211_MGD_STATE_PROBE; wk->auth_alg = auth_alg; + wk->timeout = jiffies; /* run right away */ /* * XXX: if still associated need to tell AP that we're going @@ -2448,6 +2449,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, wk->state = IEEE80211_MGD_STATE_ASSOC; wk->tries = 0; + wk->timeout = jiffies; /* run right away */ if (req->use_mfp) { ifmgd->mfp = IEEE80211_MFP_REQUIRED; -- cgit v1.1 From 91a3bd76155085d41520cf41ede39e8b7f01aeff Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 23 Jul 2009 16:37:47 -0700 Subject: mac80211: fix MLME issuing of probe requests while scanning We were issuing probe requests to the associated AP on the wrong band by having our beacon timer loss trigger while we are scanning. When we would scan the timer could hit and force us to send a probe request to the AP but with a chance we'd be on the wrong band. This leads to finding no usable bitrate but we should not get so far on the xmit path. We should not be trying to send these probe request frames so prevent ieee80211_mgd_probe_ap() from sending these. As it turns out all callers of ieee80211_mgd_probe_ap() need this check so we just move the scan check there. This means we can remove the recenlty added check during ieee80211_sta_monitor_work(). Additionally we now fix a race condition added by the patch "mac80211: do not monitor the connection while scanning" which had the same check in ieee80211_sta_conn_mon_timer(). The race happens because the timer routine *does* a valid check for scanning but after it queues work into the mac80211 workqueue the work callback can kick off with scanning enabled and cause the same issue we were trying to avoid. The more appropriate solution would be to disable the respective timers during scan and re-enable them after scan but requires more complex code and testing. Cc: Christian Lamparter Cc: Larry Finger Reported-by: Fabio Rossi Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 807ab89..76c03da 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1166,6 +1166,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, if (!netif_running(sdata->dev)) return; + if (sdata->local->scanning) + return; + mutex_lock(&ifmgd->mtx); if (!ifmgd->associated) @@ -2213,9 +2216,6 @@ static void ieee80211_sta_monitor_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.monitor_work); - if (sdata->local->scanning) - return; - ieee80211_mgd_probe_ap(sdata, false); } -- cgit v1.1 From 3fa52056f3a8e755708241d5795e6d3e6f55ad85 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Jul 2009 13:23:09 +0200 Subject: mac80211: fix PS-poll response, race When a station queries us for a PS-poll response, we wrongly queue the frame on the virtual interface's queue rather than the pending queue. Additionally, fix a race condition where we could potentially send multiple frames to the sleeping station due to using a station flag rather than a packet flag. When converting to a packet flag, we can also convert p54 and remove the filter clearing we added for it. (Also remove a now dead function) Signed-off-by: Johannes Berg Reported-by: Bob Copeland Tested-by: Bob Copeland Cc: Christian Lamparter Signed-off-by: John W. Linville --- net/mac80211/rx.c | 11 ++++++----- net/mac80211/sta_info.h | 13 ------------- net/mac80211/tx.c | 19 +------------------ 3 files changed, 7 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index cb95a31..f195705 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -783,7 +783,7 @@ static void ap_sta_ps_start(struct sta_info *sta) struct ieee80211_local *local = sdata->local; atomic_inc(&sdata->bss->num_sta_ps); - set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL); + set_sta_flags(sta, WLAN_STA_PS); drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n", @@ -799,7 +799,7 @@ static int ap_sta_ps_end(struct sta_info *sta) atomic_dec(&sdata->bss->num_sta_ps); - clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL); + clear_sta_flags(sta, WLAN_STA_PS); drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta); if (!skb_queue_empty(&sta->ps_tx_buf)) @@ -1117,14 +1117,15 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) skb_queue_empty(&rx->sta->ps_tx_buf); if (skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; /* - * Tell TX path to send one frame even though the STA may + * Tell TX path to send this frame even though the STA may * still remain is PS mode after this frame exchange. */ - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", @@ -1139,7 +1140,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) else hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - dev_queue_xmit(skb); + ieee80211_add_pending_skb(rx->local, skb); if (no_pending_pkts) sta_info_clear_tim_bit(rx->sta); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4ecf10a..ccc3adf 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -30,7 +30,6 @@ * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP. * @WLAN_STA_WME: Station is a QoS-STA. * @WLAN_STA_WDS: Station is one of our WDS peers. - * @WLAN_STA_PSPOLL: Station has just PS-polled us. * @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. @@ -47,7 +46,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_ASSOC_AP = 1<<5, WLAN_STA_WME = 1<<6, WLAN_STA_WDS = 1<<7, - WLAN_STA_PSPOLL = 1<<8, WLAN_STA_CLEAR_PS_FILT = 1<<9, WLAN_STA_MFP = 1<<10, WLAN_STA_SUSPEND = 1<<11 @@ -359,17 +357,6 @@ static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) spin_unlock_irqrestore(&sta->flaglock, irqfl); } -static inline void set_and_clear_sta_flags(struct sta_info *sta, - const u32 set, const u32 clear) -{ - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - sta->flags |= set; - sta->flags &= ~clear; - spin_unlock_irqrestore(&sta->flaglock, irqfl); -} - static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) { u32 ret; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 70ff4f0..edacad1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -373,7 +373,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) staflags = get_sta_flags(sta); if (unlikely((staflags & WLAN_STA_PS) && - !(staflags & WLAN_STA_PSPOLL))) { + !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " "before %d)\n", @@ -412,24 +412,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) sta->sta.addr); } #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { - /* - * The sleeping station with pending data is now snoozing. - * It queried us for its buffered frames and will go back - * to deep sleep once it got everything. - * - * inform the driver, in case the hardware does powersave - * frame filtering and keeps a station blacklist on its own - * (e.g: p54), so that frames can be delivered unimpeded. - * - * Note: It should be safe to disable the filter now. - * As, it is really unlikely that we still have any pending - * frame for this station in the hw's buffers/fifos left, - * that is not rejected with a unsuccessful tx_status yet. - */ - info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - } return TX_CONTINUE; } -- cgit v1.1 From 21f5fc75deca63bc41c9d13007d35981d4485622 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 24 Jul 2009 19:57:25 -0400 Subject: mac80211: fix oops due to missing private data This was caused by patch: "mac80211: cooperate more with network namespaces" The version of the patch applied doesn't match Johannes' latest: http://johannes.sipsolutions.net/patches/kernel/all/LATEST/NNN-mac80211-netns.patch The skb->cb virtual interface data wasn't being reset for reuse so ath9k pooped out when trying to dereference the private rate control info from the skb. BUG: unable to handle kernel NULL pointer dereference RIP: 0010:[] ath_tx_rc_status+0x33/0x150 [ath9k] <-- snip etc --> Reported-by: Davide Pesavento Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/tx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index edacad1..9e5dff1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1170,13 +1170,15 @@ static int __ieee80211_tx(struct ieee80211_local *local, } ret = drv_tx(local, skb); - info->control.vif = &sdata->vif; if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { dev_kfree_skb(skb); ret = NETDEV_TX_OK; } - if (ret != NETDEV_TX_OK) + if (ret != NETDEV_TX_OK) { + info->control.vif = &sdata->vif; return IEEE80211_TX_AGAIN; + } + *skbp = skb = next; ieee80211_led_tx(local, 1); fragm = true; -- cgit v1.1 From bc43b28c10855aa56f6d0bd64ec6a6d8edbcf11b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 25 Jul 2009 10:54:13 +0200 Subject: cfg80211: fix circular lock dependency (1) Luis reported this lockdep complaint, that he had also reported earlier but when trying to analyse I had been locking at the wrong code, and never saw the problem: (slightly abridged) ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.31-rc4-wl #6 ------------------------------------------------------- wpa_supplicant/3799 is trying to acquire lock: (cfg80211_mutex){+.+.+.}, at: [] cfg80211_get_dev_from_ifindex+0x1a/0x90 [cfg80211] but task is already holding lock: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x12/0x20 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (rtnl_mutex){+.+.+.}: [] __lock_acquire+0xd76/0x12b0 [] lock_acquire+0xe3/0x120 [] mutex_lock_nested+0x44/0x350 [] rtnl_lock+0x12/0x20 [] nl80211_send_reg_change_event+0x1f5/0x2a0 [cfg80211] [] set_regdom+0x28e/0x4c0 [cfg80211] -> #0 (cfg80211_mutex){+.+.+.}: [] __lock_acquire+0xe3b/0x12b0 [] lock_acquire+0xe3/0x120 [] mutex_lock_nested+0x44/0x350 [] cfg80211_get_dev_from_ifindex+0x1a/0x90 [cfg80211] [] get_rdev_dev_by_info_ifindex+0x6f/0xa0 [cfg80211] [] nl80211_set_interface+0x3b/0x260 [cfg80211] When looking at the correct code, the problem is quite obvious. I'm not entirely sure which code paths lead here, so until I can analyse it better let's just use RCU to avoid the problem. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7880a9c..283f1a8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4517,10 +4517,10 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) return; } - rtnl_lock(); + rcu_read_lock(); genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, - GFP_KERNEL); - rtnl_unlock(); + GFP_ATOMIC); + rcu_read_unlock(); return; -- cgit v1.1 From 8d8b261a5c11bd043b9b0e0c7e6c49d57611e3ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 25 Jul 2009 11:58:36 +0200 Subject: mac80211: fix receiving deauth Marcel reported a warning, which quite obviously comes from an oversight in the code handling deauth frames, and which resulted in multiple follow-up warnings due to this missing handling. This patch adds the missing deauth handling (telling cfg80211 about it) and also removes the follow-up warnings since they could happen due to races even if nothing is wrong. I've explained the races in the comments. Signed-off-by: Johannes Berg Reported-by: Marcel Holtmann Tested-by: Marcel Holtmann Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 76c03da..f60a831 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2003,6 +2003,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, case RX_MGMT_CFG80211_ASSOC: cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len); break; + case RX_MGMT_CFG80211_DEAUTH: + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, NULL); + break; default: WARN(1, "unexpected: %d", rma); } @@ -2498,8 +2501,13 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, } } - /* cfg80211 should catch this... */ - if (WARN_ON(!bssid)) { + /* + * cfg80211 should catch this ... but it's racy since + * we can receive a deauth frame, process it, hand it + * to cfg80211 while that's in a locked section already + * trying to tell us that the user wants to disconnect. + */ + if (!bssid) { mutex_unlock(&ifmgd->mtx); return -ENOLINK; } @@ -2524,8 +2532,13 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, mutex_lock(&ifmgd->mtx); - /* cfg80211 should catch that */ - if (WARN_ON(&ifmgd->associated->cbss != req->bss)) { + /* + * cfg80211 should catch this ... but it's racy since + * we can receive a disassoc frame, process it, hand it + * to cfg80211 while that's in a locked section already + * trying to tell us that the user wants to disconnect. + */ + if (&ifmgd->associated->cbss != req->bss) { mutex_unlock(&ifmgd->mtx); return -ENOLINK; } -- cgit v1.1 From c0b2bbd833a5a26b5a9425cb9a7eb66bbe736dd1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 25 Jul 2009 16:54:36 +0200 Subject: nl80211: add missing parameter clearing Jouni and Maxim reported an oops when using wpa_supplicant -Dnl80211, which seems to be due to random data being contained in the crypto settings for the assoc() command. This seems to be due to the missing memset here, so add it -- it's certainly missing but I'm not 100% certain that it will fix the problem. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 283f1a8..c951eb2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3385,6 +3385,8 @@ static int nl80211_crypto_settings(struct genl_info *info, struct cfg80211_crypto_settings *settings, int cipher_limit) { + memset(settings, 0, sizeof(*settings)); + settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { -- cgit v1.1 From 0ee9c13c7c92581ab005d80795cf65897213b249 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Sat, 25 Jul 2009 17:25:51 +0200 Subject: mac80211: fix an oops in ieee80211_scan_state_set_channel Fix an oops in ieee80211_scan_state_set_channel which was triggered if the last scanned channel was skipped (for example due to regulatory restrictions) by returning to the decision state after each skipped channel. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- net/mac80211/scan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index b376775..147772a 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -605,8 +605,11 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, /* advance state machine to next channel/band */ local->scan_channel_idx++; - if (skip) + if (skip) { + /* if we skip this channel return to the decision state */ + local->next_scan_state = SCAN_DECISION; return; + } /* * Probe delay is used to update the NAV, cf. 11.1.3.2.2 -- cgit v1.1 From f9d6b402603a63b5e5b56bd7a79fa72a818be55b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 10:22:28 +0200 Subject: cfg80211: fix disassoc while not associated When trying to disassociate while not associated, the kernel would crash rather than refusing the operation, fix this; Reported-by: Maxim Levitsky Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5b9b221..30058a8 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -545,6 +545,12 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); + if (wdev->sme_state != CFG80211_SME_CONNECTED) + return -ENOTCONN; + + if (WARN_ON(!wdev->current_bss)) + return -ENOTCONN; + memset(&req, 0, sizeof(req)); req.reason_code = reason; req.ie = ie; -- cgit v1.1 From a7bc376c858e0e724b8cb2db09b6874562d377ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 10:33:31 +0200 Subject: mac80211: verify info->control.vif is not NULL When enqueuing packets on the internal packet queue, we need to ensure that we have a valid vif pointer since that is required since the net namespace work. Add some assertions to verify this, but also don't crash is for some reason we don't end up with a vif pointer -- warn and drop the packet in all these cases. Since this code touches a number of hotpaths, it is intended to be temporary, or maybe configurable in the future, at least the bit that is in the path that gets hit for every packet, ieee80211_tx_pending(). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 5 +++++ net/mac80211/util.c | 13 +++++++++++++ 2 files changed, 18 insertions(+) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9e5dff1..4e1b2ba 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1889,6 +1889,11 @@ void ieee80211_tx_pending(unsigned long data) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sub_if_data *sdata; + if (WARN_ON(!info->control.vif)) { + kfree_skb(skb); + continue; + } + sdata = vif_to_sdata(info->control.vif); dev_hold(sdata->dev); spin_unlock_irqrestore(&local->queue_stop_reason_lock, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7fc5584..8502936 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -336,6 +336,12 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, struct ieee80211_hw *hw = &local->hw; unsigned long flags; int queue = skb_get_queue_mapping(skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (WARN_ON(!info->control.vif)) { + kfree(skb); + return; + } spin_lock_irqsave(&local->queue_stop_reason_lock, flags); __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); @@ -358,6 +364,13 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); while ((skb = skb_dequeue(skbs))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (WARN_ON(!info->control.vif)) { + kfree(skb); + continue; + } + ret++; queue = skb_get_queue_mapping(skb); __skb_queue_tail(&local->pending[queue], skb); -- cgit v1.1 From 0e82ffe3b90bcad72cfe80e4379946b8fb0691ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 12:01:50 +0200 Subject: cfg80211: combine iwfreq implementations Until now we implemented iwfreq for managed mode, we needed to keep the implementations separate, but now that we have all versions implemented we can combine them and export just one handler. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wext.c | 73 ++-------------------------------------------- net/wireless/core.h | 3 ++ net/wireless/ibss.c | 5 +--- net/wireless/nl80211.c | 2 ++ net/wireless/wext-compat.c | 54 +++++++++++++++++++++++++++++++++- net/wireless/wext-compat.h | 21 +++++++++++++ net/wireless/wext-sme.c | 5 +--- 7 files changed, 83 insertions(+), 80 deletions(-) create mode 100644 net/wireless/wext-compat.h (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5acb814..7cd9aa7 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,75 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct ieee80211_channel *chan; - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); - - /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ - if (freq->e == 0) { - if (freq->m < 0) - return -EINVAL; - else - chan = ieee80211_get_channel(local->hw.wiphy, - ieee80211_channel_to_frequency(freq->m)); - } else { - int i, div = 1000000; - for (i = 0; i < freq->e; i++) - div /= 10; - if (div <= 0) - return -EINVAL; - chan = ieee80211_get_channel(local->hw.wiphy, freq->m / div); - } - - if (!chan) - return -EINVAL; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - - /* - * no change except maybe auto -> fixed, ignore the HT - * setting so you can fix a channel you're on already - */ - if (local->oper_channel == chan) - return 0; - - local->oper_channel = chan; - local->oper_channel_type = NL80211_CHAN_NO_HT; - ieee80211_hw_config(local, 0); - - return 0; -} - - -static int ieee80211_ioctl_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); - - freq->m = local->oper_channel->center_freq; - freq->e = 6; - - return 0; -} - - static int ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) @@ -173,8 +104,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ (iw_handler) NULL, /* SIOCSIWNWID */ (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) cfg80211_wext_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) cfg80211_wext_giwfreq, /* SIOCGIWFREQ */ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ (iw_handler) NULL, /* SIOCSIWSENS */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 4276b70..6d903c1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -66,6 +66,9 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; + /* current channel */ + struct ieee80211_channel *channel; + #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ struct wiphy_debugfsdentries { diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 8b65e21..de9ac49 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -7,6 +7,7 @@ #include #include #include +#include "wext-compat.h" #include "nl80211.h" @@ -312,8 +313,6 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, @@ -342,8 +341,6 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, /* no channel if not joining */ return -EINVAL; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq); int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c951eb2..0cd5482 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -757,6 +757,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) channel_type); if (result) goto bad_res; + + rdev->channel = chan; } changed = 0; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c7351a9..fc2e776 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -14,6 +14,7 @@ #include #include #include +#include "wext-compat.h" #include "core.h" int cfg80211_wext_giwname(struct net_device *dev, @@ -300,7 +301,6 @@ struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, return ERR_PTR(-EINVAL); return chan; } -EXPORT_SYMBOL_GPL(cfg80211_wext_freq); int cfg80211_wext_siwrts(struct net_device *dev, struct iw_request_info *info, @@ -759,6 +759,58 @@ int cfg80211_wext_giwencode(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); +int cfg80211_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + int err; + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); + default: + chan = cfg80211_wext_freq(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (IS_ERR(chan)) + return PTR_ERR(chan); + err = rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + if (err) + return err; + rdev->channel = chan; + return 0; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); + +int cfg80211_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + default: + if (!rdev->channel) + return -EINVAL; + freq->m = rdev->channel->center_freq; + freq->e = 6; + return 0; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwfreq); + int cfg80211_wext_siwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h new file mode 100644 index 0000000..23a6b5a --- /dev/null +++ b/net/wireless/wext-compat.h @@ -0,0 +1,21 @@ +#ifndef __WEXT_COMPAT +#define __WEXT_COMPAT + +int cfg80211_ibss_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_ibss_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra); + +struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, + struct iw_freq *freq); + +#endif /* __WEXT_COMPAT */ diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 4c689fd..509279a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -8,6 +8,7 @@ #include #include #include +#include "wext-compat.h" #include "nl80211.h" int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, @@ -108,8 +109,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, cfg80211_unlock_rdev(rdev); return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, @@ -138,8 +137,6 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, /* no channel if not joining */ return -EINVAL; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq); int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_request_info *info, -- cgit v1.1 From 562e482265ac4d660d9f0114419591d62f44361d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 12:01:51 +0200 Subject: cfg80211: combine IWAP handlers Since we now have IWAP handlers for all modes, we can combine them into one. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wext.c | 41 ++---------------------------------- net/wireless/ibss.c | 4 ---- net/wireless/wext-compat.c | 52 +++++++++++++++++++++++++++++++++++++++------- net/wireless/wext-compat.h | 12 +++++++++++ net/wireless/wext-sme.c | 4 ---- 5 files changed, 58 insertions(+), 55 deletions(-) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 7cd9aa7..72866c8 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -59,43 +59,6 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, } -static int ieee80211_ioctl_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - - if (sdata->vif.type == NL80211_IFTYPE_WDS) - return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); - return -EOPNOTSUPP; -} - - -static int ieee80211_ioctl_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); - - if (sdata->vif.type == NL80211_IFTYPE_WDS) - return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); - - return -EOPNOTSUPP; -} - - /* Structures to export the Wireless Handlers */ static const iw_handler ieee80211_handler[] = @@ -120,8 +83,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) NULL, /* SIOCGIWSPY */ (iw_handler) NULL, /* SIOCSIWTHRSPY */ (iw_handler) NULL, /* SIOCGIWTHRSPY */ - (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ - (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) cfg80211_wext_siwap, /* SIOCSIWAP */ + (iw_handler) cfg80211_wext_giwap, /* SIOCGIWAP */ (iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */ (iw_handler) NULL, /* SIOCGIWAPLIST */ (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index de9ac49..f955225 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -466,8 +466,6 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, @@ -493,6 +491,4 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, return 0; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap); #endif diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index fc2e776..c27774b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1149,9 +1149,9 @@ int cfg80211_wext_giwpower(struct net_device *dev, } EXPORT_SYMBOL_GPL(cfg80211_wext_giwpower); -int cfg80211_wds_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) +static int cfg80211_wds_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); @@ -1177,11 +1177,10 @@ int cfg80211_wds_wext_siwap(struct net_device *dev, return 0; } -EXPORT_SYMBOL_GPL(cfg80211_wds_wext_siwap); -int cfg80211_wds_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) +static int cfg80211_wds_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -1193,7 +1192,6 @@ int cfg80211_wds_wext_giwap(struct net_device *dev, return 0; } -EXPORT_SYMBOL_GPL(cfg80211_wds_wext_giwap); int cfg80211_wext_siwrate(struct net_device *dev, struct iw_request_info *info, @@ -1327,3 +1325,41 @@ struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) return &wstats; } EXPORT_SYMBOL_GPL(cfg80211_wireless_stats); + +int cfg80211_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_WDS: + return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwap); + +int cfg80211_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); + case NL80211_IFTYPE_WDS: + return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwap); diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 23a6b5a..51028eb 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -7,6 +7,12 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, int cfg80211_ibss_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); +int cfg80211_ibss_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_ibss_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, @@ -14,6 +20,12 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, int cfg80211_mgd_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra); struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 509279a..1aa31cc 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -273,8 +273,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); int cfg80211_mgd_wext_giwap(struct net_device *dev, struct iw_request_info *info, @@ -299,8 +297,6 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, return 0; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap); int cfg80211_wext_siwgenie(struct net_device *dev, struct iw_request_info *info, -- cgit v1.1 From 1f9298f96082692bdfe73af6fc2167f627f21647 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 12:01:52 +0200 Subject: cfg80211: combine IWESSID handlers Since we now have handlers IWESSID for all modes, we can combine them into one. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wext.c | 35 ++--------------------------------- net/wireless/ibss.c | 4 ---- net/wireless/wext-compat.c | 34 ++++++++++++++++++++++++++++++++++ net/wireless/wext-compat.h | 12 ++++++++++++ net/wireless/wext-sme.c | 4 ---- 5 files changed, 48 insertions(+), 41 deletions(-) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 72866c8..aa250c3 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,37 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); - - return -EOPNOTSUPP; -} - - -static int ieee80211_ioctl_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - else if (sdata->vif.type == NL80211_IFTYPE_STATION) - return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); - - return -EOPNOTSUPP; -} - /* Structures to export the Wireless Handlers */ @@ -89,8 +58,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) NULL, /* SIOCGIWAPLIST */ (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) cfg80211_wext_siwessid, /* SIOCSIWESSID */ + (iw_handler) cfg80211_wext_giwessid, /* SIOCGIWESSID */ (iw_handler) NULL, /* SIOCSIWNICKN */ (iw_handler) NULL, /* SIOCGIWNICKN */ (iw_handler) NULL, /* -- hole -- */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index f955225..4d7a084 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -381,8 +381,6 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); int cfg80211_ibss_wext_giwessid(struct net_device *dev, struct iw_request_info *info, @@ -410,8 +408,6 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, return 0; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid); int cfg80211_ibss_wext_siwap(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c27774b..083e4c3 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1363,3 +1363,37 @@ int cfg80211_wext_giwap(struct net_device *dev, } } EXPORT_SYMBOL_GPL(cfg80211_wext_giwap); + +int cfg80211_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwessid); + +int cfg80211_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); + case NL80211_IFTYPE_STATION: + return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwessid); diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 51028eb..c0310d9 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -13,6 +13,12 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, int cfg80211_ibss_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); +int cfg80211_ibss_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_ibss_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, @@ -26,6 +32,12 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, int cfg80211_mgd_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra); +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid); struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 1aa31cc..7bacbd1 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -192,8 +192,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); return err; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct iw_request_info *info, @@ -218,8 +216,6 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, return 0; } -/* temporary symbol - mark GPL - in the future the handler won't be */ -EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid); int cfg80211_mgd_wext_siwap(struct net_device *dev, struct iw_request_info *info, -- cgit v1.1 From a9a11622c5c742c115fad371c0397ae86dd3bb67 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 12:01:53 +0200 Subject: cfg80211: self-contained wext handling where possible Finally! This is what you've all been waiting for! This patch makes cfg80211 take care of wext emulation _completely_ by itself, drivers that don't need things cfg80211 doesn't do yet don't even need to be aware of wireless extensions. This means we can also clean up mac80211's and iwm's Kconfig and make it possible to build them w/o wext now! RIP wext. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 1 - net/mac80211/Makefile | 1 - net/mac80211/ieee80211_i.h | 4 -- net/mac80211/iface.c | 1 - net/mac80211/scan.c | 1 - net/mac80211/wext.c | 98 ---------------------------------------------- net/wireless/core.c | 3 ++ net/wireless/mlme.c | 2 + net/wireless/scan.c | 1 + net/wireless/sme.c | 2 + net/wireless/wext-compat.c | 40 +++++++++++++++++++ net/wireless/wext-compat.h | 5 +++ 12 files changed, 53 insertions(+), 106 deletions(-) delete mode 100644 net/mac80211/wext.c (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 19a4c66..7dd77b6 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -6,7 +6,6 @@ config MAC80211 select CRYPTO_ARC4 select CRYPTO_AES select CRC32 - select WIRELESS_EXT ---help--- This option enables the hardware independent IEEE 802.11 networking stack. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 91284a7..9f3cf71 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_MAC80211) += mac80211.o # mac80211 objects mac80211-y := \ main.o \ - wext.o \ sta_info.o \ wep.o \ wpa.o \ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c6b25cb..aec6853 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "key.h" #include "sta_info.h" @@ -951,9 +950,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_configure_filter(struct ieee80211_local *local); u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); -/* wireless extensions */ -extern const struct iw_handler_def ieee80211_iw_handler_def; - /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d79a211..6c655b6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -684,7 +684,6 @@ static void ieee80211_if_setup(struct net_device *dev) { ether_setup(dev); dev->netdev_ops = &ieee80211_dataif_ops; - dev->wireless_handlers = &ieee80211_iw_handler_def; dev->destructor = free_netdev; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 147772a..4573100 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "ieee80211_i.h" #include "driver-ops.h" diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c deleted file mode 100644 index aa250c3..0000000 --- a/net/mac80211/wext.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2002-2005, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "ieee80211_i.h" -#include "led.h" -#include "rate.h" -#include "wpa.h" -#include "aes_ccm.h" - - - -/* Structures to export the Wireless Handlers */ - -static const iw_handler ieee80211_handler[] = -{ - (iw_handler) NULL, /* SIOCSIWCOMMIT */ - (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) cfg80211_wext_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) cfg80211_wext_giwfreq, /* SIOCGIWFREQ */ - (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ - (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ - (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ - (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ - (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* SIOCSIWTHRSPY */ - (iw_handler) NULL, /* SIOCGIWTHRSPY */ - (iw_handler) cfg80211_wext_siwap, /* SIOCSIWAP */ - (iw_handler) cfg80211_wext_giwap, /* SIOCGIWAP */ - (iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ - (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ - (iw_handler) cfg80211_wext_siwessid, /* SIOCSIWESSID */ - (iw_handler) cfg80211_wext_giwessid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) NULL, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) cfg80211_wext_siwrate, /* SIOCSIWRATE */ - (iw_handler) cfg80211_wext_giwrate, /* SIOCGIWRATE */ - (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ - (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ - (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ - (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */ - (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */ - (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */ - (iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */ - (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ - (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ - (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ - (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */ - (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ - (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ - (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) NULL, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ - (iw_handler) NULL, /* -- hole -- */ -}; - -const struct iw_handler_def ieee80211_iw_handler_def = -{ - .num_standard = ARRAY_SIZE(ieee80211_handler), - .standard = (iw_handler *) ieee80211_handler, - .get_wireless_stats = cfg80211_wireless_stats, -}; diff --git a/net/wireless/core.c b/net/wireless/core.c index 442c9f3..f9fee65 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -19,6 +19,7 @@ #include "core.h" #include "sysfs.h" #include "debugfs.h" +#include "wext-compat.h" /* name for sysfs, %d is appended */ #define PHY_NAME "phy" @@ -665,6 +666,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev->sme_state = CFG80211_SME_IDLE; mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_WIRELESS_EXT + if (!dev->wireless_handlers) + dev->wireless_handlers = &cfg80211_wext_handler; wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 30058a8..097a87d 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include "core.h" #include "nl80211.h" diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2a2683f..67714d7 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -14,6 +14,7 @@ #include #include "core.h" #include "nl80211.h" +#include "wext-compat.h" #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a197410..d2b5d4c 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include "nl80211.h" diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 083e4c3..e4e90e2 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1397,3 +1397,43 @@ int cfg80211_wext_giwessid(struct net_device *dev, } } EXPORT_SYMBOL_GPL(cfg80211_wext_giwessid); + +static const iw_handler cfg80211_handlers[] = { + [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname, + [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq, + [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq, + [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode, + [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode, + [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange, + [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap, + [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap, + [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme, + [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan, + [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan, + [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid, + [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid, + [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate, + [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate, + [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts, + [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts, + [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag, + [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag, + [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower, + [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower, + [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry, + [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry, + [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode, + [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode, + [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower, + [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower, + [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, + [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth, + [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth, + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext, +}; + +const struct iw_handler_def cfg80211_wext_handler = { + .num_standard = ARRAY_SIZE(cfg80211_handlers), + .standard = cfg80211_handlers, + .get_wireless_stats = cfg80211_wireless_stats, +}; diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index c0310d9..9a37747 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -1,6 +1,9 @@ #ifndef __WEXT_COMPAT #define __WEXT_COMPAT +#include +#include + int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra); @@ -42,4 +45,6 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); + +extern const struct iw_handler_def cfg80211_wext_handler; #endif /* __WEXT_COMPAT */ -- cgit v1.1 From b4076d1715b8a38138db0805a85932b3b650d583 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 30 Jul 2009 13:16:45 -0700 Subject: vlan: allow creating vlan when real device is not up There is no reason for the arbitrary restriction that device must be up to create a vlan. This patch was added to Vyatta kernel to resolve startup ordering issues where vlan's are created but real device was disabled. Note: the vlan already correctly inherits the operstate from real device; so if vlan is created and real device is marked down, the vlan is marked down. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/8021q/vlan.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6d37b7e..a1f1630 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -225,12 +225,6 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) return -EOPNOTSUPP; } - /* The real device must be up and operating in order to - * assosciate a VLAN device with it. - */ - if (!(real_dev->flags & IFF_UP)) - return -ENETDOWN; - if (__find_vlan_dev(real_dev, vlan_id) != NULL) return -EEXIST; -- cgit v1.1 From 9aada7ac047f789ffb27540cc1695989897b2dfe Mon Sep 17 00:00:00 2001 From: Hannes Eder Date: Thu, 30 Jul 2009 14:29:44 -0700 Subject: IPVS: use pr_fmt While being at it cleanup whitespace. Signed-off-by: Hannes Eder Signed-off-by: David S. Miller --- net/netfilter/ipvs/ip_vs_app.c | 3 +++ net/netfilter/ipvs/ip_vs_conn.c | 3 +++ net/netfilter/ipvs/ip_vs_core.c | 3 +++ net/netfilter/ipvs/ip_vs_ctl.c | 3 +++ net/netfilter/ipvs/ip_vs_dh.c | 3 +++ net/netfilter/ipvs/ip_vs_est.c | 4 ++++ net/netfilter/ipvs/ip_vs_ftp.c | 3 +++ net/netfilter/ipvs/ip_vs_lblc.c | 3 +++ net/netfilter/ipvs/ip_vs_lblcr.c | 3 +++ net/netfilter/ipvs/ip_vs_lc.c | 3 +++ net/netfilter/ipvs/ip_vs_nq.c | 3 +++ net/netfilter/ipvs/ip_vs_proto.c | 7 +++++-- net/netfilter/ipvs/ip_vs_proto_ah_esp.c | 7 +++++-- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 +++ net/netfilter/ipvs/ip_vs_proto_udp.c | 3 +++ net/netfilter/ipvs/ip_vs_rr.c | 3 +++ net/netfilter/ipvs/ip_vs_sched.c | 3 +++ net/netfilter/ipvs/ip_vs_sed.c | 3 +++ net/netfilter/ipvs/ip_vs_sh.c | 3 +++ net/netfilter/ipvs/ip_vs_sync.c | 3 +++ net/netfilter/ipvs/ip_vs_wlc.c | 3 +++ net/netfilter/ipvs/ip_vs_wrr.c | 3 +++ net/netfilter/ipvs/ip_vs_xmit.c | 3 +++ 23 files changed, 74 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 201b8ea..c1781f8 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -18,6 +18,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 77bfdfe..4173d7b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -22,6 +22,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 8dddb17..6811dca 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -24,6 +24,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 2d24d81..e6133ea 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -18,6 +18,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index a9dac74..d0c0594 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -35,6 +35,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 2eb2860..702b53c 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -11,6 +11,10 @@ * Changes: * */ + +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 428edbf..9c16a3f 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -22,6 +22,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 3eb5e26..98fb185 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -39,6 +39,9 @@ * me to write this module. */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index c04ce56..5f5e5f4 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -37,6 +37,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index d0dadc8..4ecd5e1 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -14,6 +14,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index 694952d..2224478 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -31,6 +31,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index a01520e..a95bc40 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -13,6 +13,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include @@ -181,7 +184,7 @@ ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp, &ih->daddr, ntohs(pptr[1])); } - printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); + pr_debug("%s: %s\n", msg, buf); } #ifdef CONFIG_IP_VS_IPV6 @@ -215,7 +218,7 @@ ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp, &ih->daddr, ntohs(pptr[1])); } - printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); + pr_debug("%s: %s\n", msg, buf); } #endif diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c index 79f56c1..c30b43c 100644 --- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c +++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c @@ -10,6 +10,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include @@ -138,7 +141,7 @@ ah_esp_debug_packet_v4(struct ip_vs_protocol *pp, const struct sk_buff *skb, sprintf(buf, "%s %pI4->%pI4", pp->name, &ih->saddr, &ih->daddr); - printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); + pr_debug("%s: %s\n", msg, buf); } #ifdef CONFIG_IP_VS_IPV6 @@ -156,7 +159,7 @@ ah_esp_debug_packet_v6(struct ip_vs_protocol *pp, const struct sk_buff *skb, sprintf(buf, "%s %pI6->%pI6", pp->name, &ih->saddr, &ih->daddr); - printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf); + pr_debug("%s: %s\n", msg, buf); } #endif diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 8cba418..c36c80d 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -13,6 +13,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include /* for tcphdr */ diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index d2930a7..96ebe40 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -13,6 +13,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index 2d16ab7..b01007e 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -19,6 +19,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index a46ad9e..87bc5ea 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -17,6 +17,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 20e4657..4f745dd 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -35,6 +35,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index 75709eb..fb4d2d2 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -32,6 +32,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 5c48378..cc04c99 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -17,6 +17,9 @@ * Justin Ossevoort : Fix endian problem on sync message size. */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index 8e94256..bbddfdb 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -19,6 +19,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index f7d74ef..c39ebb6 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -18,6 +18,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 5874657..061e76d 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -13,6 +13,9 @@ * */ +#define KMSG_COMPONENT "IPVS" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include /* for tcphdr */ #include -- cgit v1.1 From a33bc5c15154c835aae26f16e6a3a7d9ad4acb45 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 30 Jul 2009 18:52:15 -0700 Subject: xfrm: select sane defaults for xfrm[4|6] gc_thresh Choose saner defaults for xfrm[4|6] gc_thresh values on init Currently, the xfrm[4|6] code has hard-coded initial gc_thresh values (set to 1024). Given that the ipv4 and ipv6 routing caches are sized dynamically at boot time, the static selections can be non-sensical. This patch dynamically selects an appropriate gc threshold based on the corresponding main routing table size, using the assumption that we should in the worst case be able to handle as many connections as the routing table can. For ipv4, the maximum route cache size is 16 * the number of hash buckets in the route cache. Given that xfrm4 starts garbage collection at the gc_thresh and prevents new allocations at 2 * gc_thresh, we set gc_thresh to half the maximum route cache size. For ipv6, its a bit trickier. there is no maximum route cache size, but the ipv6 dst_ops gc_thresh is statically set to 1024. It seems sane to select a simmilar gc_thresh for the xfrm6 code that is half the number of hash buckets in the v6 route cache times 16 (like the v4 code does). Signed-off-by: Neil Horman Signed-off-by: David S. Miller --- net/ipv4/route.c | 2 +- net/ipv4/xfrm4_policy.c | 13 ++++++++++++- net/ipv6/ip6_fib.c | 16 +++++----------- net/ipv6/xfrm6_policy.c | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 278f46f..fafbe16 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3442,7 +3442,7 @@ int __init ip_rt_init(void) printk(KERN_ERR "Unable to create route proc files\n"); #ifdef CONFIG_XFRM xfrm_init(); - xfrm4_init(); + xfrm4_init(ip_rt_max_size); #endif rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 26496ba..1ba4474 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -290,10 +290,21 @@ static void __exit xfrm4_policy_fini(void) xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); } -void __init xfrm4_init(void) +void __init xfrm4_init(int rt_max_size) { xfrm4_state_init(); xfrm4_policy_init(); + /* + * Select a default value for the gc_thresh based on the main route + * table hash size. It seems to me the worst case scenario is when + * we have ipsec operating in transport mode, in which we create a + * dst_entry per socket. The xfrm gc algorithm starts trying to remove + * entries at gc_thresh, and prevents new allocations as 2*gc_thresh + * so lets set an initial xfrm gc_thresh value at the rt_max_size/2. + * That will let us store an ipsec connection per route table entry, + * and start cleaning when were 1/2 full + */ + xfrm4_dst_ops.gc_thresh = rt_max_size/2; sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv4_ctl_path, xfrm4_policy_table); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 52ee1dc..0e93ca5 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -164,12 +164,6 @@ static __inline__ void rt6_release(struct rt6_info *rt) dst_free(&rt->u.dst); } -#ifdef CONFIG_IPV6_MULTIPLE_TABLES -#define FIB_TABLE_HASHSZ 256 -#else -#define FIB_TABLE_HASHSZ 1 -#endif - static void fib6_link_table(struct net *net, struct fib6_table *tb) { unsigned int h; @@ -180,7 +174,7 @@ static void fib6_link_table(struct net *net, struct fib6_table *tb) */ rwlock_init(&tb->tb6_lock); - h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1); + h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1); /* * No protection necessary, this is the only list mutatation @@ -231,7 +225,7 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id) if (id == 0) id = RT6_TABLE_MAIN; - h = id & (FIB_TABLE_HASHSZ - 1); + h = id & (FIB6_TABLE_HASHSZ - 1); rcu_read_lock(); head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) { @@ -382,7 +376,7 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) arg.net = net; w->args = &arg; - for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { e = 0; head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry(tb, node, head, tb6_hlist) { @@ -1368,7 +1362,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), unsigned int h; rcu_read_lock(); - for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(table, node, head, tb6_hlist) { write_lock_bh(&table->tb6_lock); @@ -1483,7 +1477,7 @@ static int fib6_net_init(struct net *net) if (!net->ipv6.rt6_stats) goto out_timer; - net->ipv6.fib_table_hash = kcalloc(FIB_TABLE_HASHSZ, + net->ipv6.fib_table_hash = kcalloc(FIB6_TABLE_HASHSZ, sizeof(*net->ipv6.fib_table_hash), GFP_KERNEL); if (!net->ipv6.fib_table_hash) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 4acc308..611cffc 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -323,6 +323,7 @@ static struct ctl_table_header *sysctl_hdr; int __init xfrm6_init(void) { int ret; + unsigned int gc_thresh; ret = xfrm6_policy_init(); if (ret) @@ -331,6 +332,20 @@ int __init xfrm6_init(void) ret = xfrm6_state_init(); if (ret) goto out_policy; + /* + * We need a good default value for the xfrm6 gc threshold. + * In ipv4 we set it to the route hash table size * 8, which + * is half the size of the maximaum route cache for ipv4. It + * would be good to do the same thing for v6, except the table is + * constructed differently here. Here each table for a net namespace + * can have FIB_TABLE_HASHSZ entries, so lets go with the same + * computation that we used for ipv4 here. Also, lets keep the initial + * gc_thresh to a minimum of 1024, since, the ipv6 route cache defaults + * to that as a minimum as well + */ + gc_thresh = FIB6_TABLE_HASHSZ * 8; + xfrm6_dst_ops.gc_thresh = (gc_thresh < 1024) ? 1024 : gc_thresh; + sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv6_ctl_path, xfrm6_policy_table); out: -- cgit v1.1 From 81e43213882e62374c1a87f9d97d4287ed9f7257 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 28 Jul 2009 09:48:07 +0000 Subject: inet6: functions shadow global variable This renames away a variable clash: * ipv6_table[] is declared as a static global table; * ipv6_sysctl_net_init() uses ipv6_table to refer/destroy dynamic memory; * ipv6_sysctl_net_exit() also uses ipv6_table for the same purpose; * both the two last functions call kfree() on ipv6_table. Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- net/ipv6/sysctl_net_ipv6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index a031034..0dc6a4e 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -40,7 +40,7 @@ static ctl_table ipv6_table_template[] = { { .ctl_name = 0 } }; -static ctl_table ipv6_table[] = { +static ctl_table ipv6_rotable[] = { { .ctl_name = NET_IPV6_MLD_MAX_MSF, .procname = "mld_max_msf", @@ -130,7 +130,7 @@ int ipv6_sysctl_register(void) { int err = -ENOMEM; - ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table); + ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_rotable); if (ip6_header == NULL) goto out; -- cgit v1.1 From 1e3e238e9c4bf9987b19185235cd0cdc21ea038c Mon Sep 17 00:00:00 2001 From: Hannes Eder Date: Sun, 2 Aug 2009 11:05:41 +0000 Subject: IPVS: use pr_err and friends instead of IP_VS_ERR and friends Since pr_err and friends are used instead of printk there is no point in keeping IP_VS_ERR and friends. Furthermore make use of '__func__' instead of hard coded function names. Signed-off-by: Hannes Eder Acked-by: Simon Horman Signed-off-by: David S. Miller --- net/netfilter/ipvs/ip_vs_app.c | 16 +++++----- net/netfilter/ipvs/ip_vs_conn.c | 14 ++++---- net/netfilter/ipvs/ip_vs_core.c | 24 +++++++------- net/netfilter/ipvs/ip_vs_ctl.c | 62 +++++++++++++++++------------------- net/netfilter/ipvs/ip_vs_dh.c | 4 +-- net/netfilter/ipvs/ip_vs_ftp.c | 4 +-- net/netfilter/ipvs/ip_vs_lblc.c | 6 ++-- net/netfilter/ipvs/ip_vs_lblcr.c | 14 ++++---- net/netfilter/ipvs/ip_vs_lc.c | 2 +- net/netfilter/ipvs/ip_vs_nq.c | 2 +- net/netfilter/ipvs/ip_vs_proto.c | 2 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 2 +- net/netfilter/ipvs/ip_vs_rr.c | 2 +- net/netfilter/ipvs/ip_vs_sched.c | 38 +++++++++++----------- net/netfilter/ipvs/ip_vs_sed.c | 2 +- net/netfilter/ipvs/ip_vs_sh.c | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 50 ++++++++++++++--------------- net/netfilter/ipvs/ip_vs_wrr.c | 4 +-- net/netfilter/ipvs/ip_vs_xmit.c | 40 +++++++++++------------ 20 files changed, 145 insertions(+), 147 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index c1781f8..3c7e427 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -265,12 +265,12 @@ static inline void vs_fix_seq(const struct ip_vs_seq *vseq, struct tcphdr *th) if (vseq->delta || vseq->previous_delta) { if(after(seq, vseq->init_seq)) { th->seq = htonl(seq + vseq->delta); - IP_VS_DBG(9, "vs_fix_seq(): added delta (%d) to seq\n", - vseq->delta); + IP_VS_DBG(9, "%s(): added delta (%d) to seq\n", + __func__, vseq->delta); } else { th->seq = htonl(seq + vseq->previous_delta); - IP_VS_DBG(9, "vs_fix_seq(): added previous_delta " - "(%d) to seq\n", vseq->previous_delta); + IP_VS_DBG(9, "%s(): added previous_delta (%d) to seq\n", + __func__, vseq->previous_delta); } } } @@ -294,14 +294,14 @@ vs_fix_ack_seq(const struct ip_vs_seq *vseq, struct tcphdr *th) to receive next, so compare it with init_seq+delta */ if(after(ack_seq, vseq->init_seq+vseq->delta)) { th->ack_seq = htonl(ack_seq - vseq->delta); - IP_VS_DBG(9, "vs_fix_ack_seq(): subtracted delta " - "(%d) from ack_seq\n", vseq->delta); + IP_VS_DBG(9, "%s(): subtracted delta " + "(%d) from ack_seq\n", __func__, vseq->delta); } else { th->ack_seq = htonl(ack_seq - vseq->previous_delta); - IP_VS_DBG(9, "vs_fix_ack_seq(): subtracted " + IP_VS_DBG(9, "%s(): subtracted " "previous_delta (%d) from ack_seq\n", - vseq->previous_delta); + __func__, vseq->previous_delta); } } } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 4173d7b..27c30cf 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -153,8 +153,8 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) atomic_inc(&cp->refcnt); ret = 1; } else { - IP_VS_ERR("ip_vs_conn_hash(): request for already hashed, " - "called from %p\n", __builtin_return_address(0)); + pr_err("%s(): request for already hashed, called from %pF\n", + __func__, __builtin_return_address(0)); ret = 0; } @@ -692,7 +692,7 @@ ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport, cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC); if (cp == NULL) { - IP_VS_ERR_RL("ip_vs_conn_new: no memory available.\n"); + IP_VS_ERR_RL("%s(): no memory\n", __func__); return NULL; } @@ -1076,10 +1076,10 @@ int __init ip_vs_conn_init(void) return -ENOMEM; } - IP_VS_INFO("Connection hash table configured " - "(size=%d, memory=%ldKbytes)\n", - IP_VS_CONN_TAB_SIZE, - (long)(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head))/1024); + pr_info("Connection hash table configured " + "(size=%d, memory=%ldKbytes)\n", + IP_VS_CONN_TAB_SIZE, + (long)(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head))/1024); IP_VS_DBG(0, "Each connection entry needs %Zd bytes at least\n", sizeof(struct ip_vs_conn)); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 6811dca..b227750 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -391,9 +391,9 @@ ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) */ if (!svc->fwmark && pptr[1] != svc->port) { if (!svc->port) - IP_VS_ERR("Schedule: port zero only supported " - "in persistent services, " - "check your ipvs configuration\n"); + pr_err("Schedule: port zero only supported " + "in persistent services, " + "check your ipvs configuration\n"); return NULL; } @@ -465,7 +465,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_service_put(svc); /* create a new connection entry */ - IP_VS_DBG(6, "ip_vs_leave: create a cache_bypass entry\n"); + IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__); cp = ip_vs_conn_new(svc->af, iph.protocol, &iph.saddr, pptr[0], &iph.daddr, pptr[1], @@ -667,8 +667,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, unsigned int verdict = NF_DROP; if (IP_VS_FWD_METHOD(cp) != 0) { - IP_VS_ERR("shouldn't reach here, because the box is on the " - "half connection in the tun/dr module.\n"); + pr_err("shouldn't reach here, because the box is on the " + "half connection in the tun/dr module.\n"); } /* Ensure the checksum is correct */ @@ -1490,7 +1490,7 @@ static int __init ip_vs_init(void) ret = ip_vs_control_init(); if (ret < 0) { - IP_VS_ERR("can't setup control.\n"); + pr_err("can't setup control.\n"); goto cleanup_estimator; } @@ -1498,23 +1498,23 @@ static int __init ip_vs_init(void) ret = ip_vs_app_init(); if (ret < 0) { - IP_VS_ERR("can't setup application helper.\n"); + pr_err("can't setup application helper.\n"); goto cleanup_protocol; } ret = ip_vs_conn_init(); if (ret < 0) { - IP_VS_ERR("can't setup connection table.\n"); + pr_err("can't setup connection table.\n"); goto cleanup_app; } ret = nf_register_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); if (ret < 0) { - IP_VS_ERR("can't register hooks.\n"); + pr_err("can't register hooks.\n"); goto cleanup_conn; } - IP_VS_INFO("ipvs loaded.\n"); + pr_info("ipvs loaded.\n"); return ret; cleanup_conn: @@ -1537,7 +1537,7 @@ static void __exit ip_vs_cleanup(void) ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); ip_vs_estimator_cleanup(); - IP_VS_INFO("ipvs unloaded.\n"); + pr_info("ipvs unloaded.\n"); } module_init(ip_vs_init); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index e6133ea..fba2892 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -343,8 +343,8 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) unsigned hash; if (svc->flags & IP_VS_SVC_F_HASHED) { - IP_VS_ERR("ip_vs_svc_hash(): request for already hashed, " - "called from %p\n", __builtin_return_address(0)); + pr_err("%s(): request for already hashed, called from %pF\n", + __func__, __builtin_return_address(0)); return 0; } @@ -377,8 +377,8 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) static int ip_vs_svc_unhash(struct ip_vs_service *svc) { if (!(svc->flags & IP_VS_SVC_F_HASHED)) { - IP_VS_ERR("ip_vs_svc_unhash(): request for unhash flagged, " - "called from %p\n", __builtin_return_address(0)); + pr_err("%s(): request for unhash flagged, called from %pF\n", + __func__, __builtin_return_address(0)); return 0; } @@ -844,7 +844,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, dest = kzalloc(sizeof(struct ip_vs_dest), GFP_ATOMIC); if (dest == NULL) { - IP_VS_ERR("ip_vs_new_dest: kmalloc failed.\n"); + pr_err("%s(): no memory.\n", __func__); return -ENOMEM; } @@ -888,13 +888,13 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) EnterFunction(2); if (udest->weight < 0) { - IP_VS_ERR("ip_vs_add_dest(): server weight less than zero\n"); + pr_err("%s(): server weight less than zero\n", __func__); return -ERANGE; } if (udest->l_threshold > udest->u_threshold) { - IP_VS_ERR("ip_vs_add_dest(): lower threshold is higher than " - "upper threshold\n"); + pr_err("%s(): lower threshold is higher than upper threshold\n", + __func__); return -ERANGE; } @@ -906,7 +906,7 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) dest = ip_vs_lookup_dest(svc, &daddr, dport); if (dest != NULL) { - IP_VS_DBG(1, "ip_vs_add_dest(): dest already exists\n"); + IP_VS_DBG(1, "%s(): dest already exists\n", __func__); return -EEXIST; } @@ -1000,13 +1000,13 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) EnterFunction(2); if (udest->weight < 0) { - IP_VS_ERR("ip_vs_edit_dest(): server weight less than zero\n"); + pr_err("%s(): server weight less than zero\n", __func__); return -ERANGE; } if (udest->l_threshold > udest->u_threshold) { - IP_VS_ERR("ip_vs_edit_dest(): lower threshold is higher than " - "upper threshold\n"); + pr_err("%s(): lower threshold is higher than upper threshold\n", + __func__); return -ERANGE; } @@ -1018,7 +1018,7 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) dest = ip_vs_lookup_dest(svc, &daddr, dport); if (dest == NULL) { - IP_VS_DBG(1, "ip_vs_edit_dest(): dest doesn't exist\n"); + IP_VS_DBG(1, "%s(): dest doesn't exist\n", __func__); return -ENOENT; } @@ -1118,7 +1118,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) dest = ip_vs_lookup_dest(svc, &udest->addr, dport); if (dest == NULL) { - IP_VS_DBG(1, "ip_vs_del_dest(): destination not found!\n"); + IP_VS_DBG(1, "%s(): destination not found!\n", __func__); return -ENOENT; } @@ -1164,8 +1164,7 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u, /* Lookup the scheduler by 'u->sched_name' */ sched = ip_vs_scheduler_get(u->sched_name); if (sched == NULL) { - IP_VS_INFO("Scheduler module ip_vs_%s not found\n", - u->sched_name); + pr_info("Scheduler module ip_vs_%s not found\n", u->sched_name); ret = -ENOENT; goto out_mod_dec; } @@ -1179,7 +1178,7 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u, svc = kzalloc(sizeof(struct ip_vs_service), GFP_ATOMIC); if (svc == NULL) { - IP_VS_DBG(1, "ip_vs_add_service: kmalloc failed.\n"); + IP_VS_DBG(1, "%s(): no memory\n", __func__); ret = -ENOMEM; goto out_err; } @@ -1262,8 +1261,7 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) */ sched = ip_vs_scheduler_get(u->sched_name); if (sched == NULL) { - IP_VS_INFO("Scheduler module ip_vs_%s not found\n", - u->sched_name); + pr_info("Scheduler module ip_vs_%s not found\n", u->sched_name); return -ENOENT; } old_sched = sched; @@ -2080,8 +2078,8 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) return -EPERM; if (len != set_arglen[SET_CMDID(cmd)]) { - IP_VS_ERR("set_ctl: len %u != %u\n", - len, set_arglen[SET_CMDID(cmd)]); + pr_err("set_ctl: len %u != %u\n", + len, set_arglen[SET_CMDID(cmd)]); return -EINVAL; } @@ -2132,9 +2130,9 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */ if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) { - IP_VS_ERR("set_ctl: invalid protocol: %d %pI4:%d %s\n", - usvc.protocol, &usvc.addr.ip, - ntohs(usvc.port), usvc.sched_name); + pr_err("set_ctl: invalid protocol: %d %pI4:%d %s\n", + usvc.protocol, &usvc.addr.ip, + ntohs(usvc.port), usvc.sched_name); ret = -EFAULT; goto out_unlock; } @@ -2359,8 +2357,8 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) return -EPERM; if (*len < get_arglen[GET_CMDID(cmd)]) { - IP_VS_ERR("get_ctl: len %u < %u\n", - *len, get_arglen[GET_CMDID(cmd)]); + pr_err("get_ctl: len %u < %u\n", + *len, get_arglen[GET_CMDID(cmd)]); return -EINVAL; } @@ -2405,7 +2403,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) size = sizeof(*get) + sizeof(struct ip_vs_service_entry) * get->num_services; if (*len != size) { - IP_VS_ERR("length: %u != %u\n", *len, size); + pr_err("length: %u != %u\n", *len, size); ret = -EINVAL; goto out; } @@ -2445,7 +2443,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) size = sizeof(*get) + sizeof(struct ip_vs_dest_entry) * get->num_dests; if (*len != size) { - IP_VS_ERR("length: %u != %u\n", *len, size); + pr_err("length: %u != %u\n", *len, size); ret = -EINVAL; goto out; } @@ -3173,7 +3171,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) else if (cmd == IPVS_CMD_GET_CONFIG) reply_cmd = IPVS_CMD_SET_CONFIG; else { - IP_VS_ERR("unknown Generic Netlink command\n"); + pr_err("unknown Generic Netlink command\n"); return -EINVAL; } @@ -3238,7 +3236,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) goto out; nla_put_failure: - IP_VS_ERR("not enough space in Netlink message\n"); + pr_err("not enough space in Netlink message\n"); ret = -EMSGSIZE; out_err: @@ -3369,13 +3367,13 @@ int __init ip_vs_control_init(void) ret = nf_register_sockopt(&ip_vs_sockopts); if (ret) { - IP_VS_ERR("cannot register sockopt.\n"); + pr_err("cannot register sockopt.\n"); return ret; } ret = ip_vs_genl_register(); if (ret) { - IP_VS_ERR("cannot register Generic Netlink interface.\n"); + pr_err("cannot register Generic Netlink interface.\n"); nf_unregister_sockopt(&ip_vs_sockopts); return ret; } diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index d0c0594..fe3e188 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -150,7 +150,7 @@ static int ip_vs_dh_init_svc(struct ip_vs_service *svc) tbl = kmalloc(sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE, GFP_ATOMIC); if (tbl == NULL) { - IP_VS_ERR("ip_vs_dh_init_svc(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return -ENOMEM; } svc->sched_data = tbl; @@ -217,7 +217,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); - IP_VS_DBG(6, "ip_vs_dh_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); tbl = (struct ip_vs_dh_bucket *)svc->sched_data; dest = ip_vs_dh_get(svc->af, tbl, &iph.daddr); diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 9c16a3f..33e2c79 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -385,8 +385,8 @@ static int __init ip_vs_ftp_init(void) ret = register_ip_vs_app_inc(app, app->protocol, ports[i]); if (ret) break; - IP_VS_INFO("%s: loaded support on port[%d] = %d\n", - app->name, i, ports[i]); + pr_info("%s: loaded support on port[%d] = %d\n", + app->name, i, ports[i]); } if (ret) diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 98fb185..c1757f3 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -202,7 +202,7 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr, if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); if (!en) { - IP_VS_ERR("ip_vs_lblc_new(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return NULL; } @@ -335,7 +335,7 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc) */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); if (tbl == NULL) { - IP_VS_ERR("ip_vs_lblc_init_svc(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return -ENOMEM; } svc->sched_data = tbl; @@ -480,7 +480,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); - IP_VS_DBG(6, "ip_vs_lblc_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* First look in our cache */ read_lock(&svc->sched_lock); diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 5f5e5f4..715b57f 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -111,7 +111,7 @@ ip_vs_dest_set_insert(struct ip_vs_dest_set *set, struct ip_vs_dest *dest) e = kmalloc(sizeof(*e), GFP_ATOMIC); if (e == NULL) { - IP_VS_ERR("ip_vs_dest_set_insert(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return NULL; } @@ -205,8 +205,9 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) } } - IP_VS_DBG_BUF(6, "ip_vs_dest_set_min: server %s:%d " + IP_VS_DBG_BUF(6, "%s(): server %s:%d " "activeconns %d refcnt %d weight %d overhead %d\n", + __func__, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), @@ -252,8 +253,9 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) } } - IP_VS_DBG_BUF(6, "ip_vs_dest_set_max: server %s:%d " + IP_VS_DBG_BUF(6, "%s(): server %s:%d " "activeconns %d refcnt %d weight %d overhead %d\n", + __func__, IP_VS_DBG_ADDR(most->af, &most->addr), ntohs(most->port), atomic_read(&most->activeconns), atomic_read(&most->refcnt), @@ -377,7 +379,7 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr, if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); if (!en) { - IP_VS_ERR("ip_vs_lblcr_new(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return NULL; } @@ -511,7 +513,7 @@ static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc) */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); if (tbl == NULL) { - IP_VS_ERR("ip_vs_lblcr_init_svc(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return -ENOMEM; } svc->sched_data = tbl; @@ -657,7 +659,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); - IP_VS_DBG(6, "ip_vs_lblcr_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* First look in our cache */ read_lock(&svc->sched_lock); diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 4ecd5e1..4f69db1 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -47,7 +47,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) struct ip_vs_dest *dest, *least = NULL; unsigned int loh = 0, doh; - IP_VS_DBG(6, "ip_vs_lc_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* * Simply select the server with the least number of diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index 2224478..c413e18 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -60,7 +60,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) struct ip_vs_dest *dest, *least = NULL; unsigned int loh = 0, doh; - IP_VS_DBG(6, "ip_vs_nq_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* * We calculate the load of each dest server as follows: diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index a95bc40..85c8892 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -262,7 +262,7 @@ int __init ip_vs_protocol_init(void) #ifdef CONFIG_IP_VS_PROTO_ESP REGISTER_PROTOCOL(&ip_vs_protocol_esp); #endif - IP_VS_INFO("Registered protocols (%s)\n", &protocols[2]); + pr_info("Registered protocols (%s)\n", &protocols[2]); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index c36c80d..2278e14 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -664,7 +664,7 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) break; spin_unlock(&tcp_app_lock); - IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" + IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", __func__, IP_VS_DBG_ADDR(cp->af, &cp->caddr), diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 96ebe40..33a05d3 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -445,7 +445,7 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp) break; spin_unlock(&udp_app_lock); - IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" + IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", __func__, IP_VS_DBG_ADDR(cp->af, &cp->caddr), diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index b01007e..e210f37 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -51,7 +51,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) struct list_head *p, *q; struct ip_vs_dest *dest; - IP_VS_DBG(6, "ip_vs_rr_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); write_lock(&svc->sched_lock); p = (struct list_head *)svc->sched_data; diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index 87bc5ea..bbc1ac7 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -47,11 +47,11 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc, int ret; if (svc == NULL) { - IP_VS_ERR("ip_vs_bind_scheduler(): svc arg NULL\n"); + pr_err("%s(): svc arg NULL\n", __func__); return -EINVAL; } if (scheduler == NULL) { - IP_VS_ERR("ip_vs_bind_scheduler(): scheduler arg NULL\n"); + pr_err("%s(): scheduler arg NULL\n", __func__); return -EINVAL; } @@ -60,7 +60,7 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc, if (scheduler->init_service) { ret = scheduler->init_service(svc); if (ret) { - IP_VS_ERR("ip_vs_bind_scheduler(): init error\n"); + pr_err("%s(): init error\n", __func__); return ret; } } @@ -77,19 +77,19 @@ int ip_vs_unbind_scheduler(struct ip_vs_service *svc) struct ip_vs_scheduler *sched; if (svc == NULL) { - IP_VS_ERR("ip_vs_unbind_scheduler(): svc arg NULL\n"); + pr_err("%s(): svc arg NULL\n", __func__); return -EINVAL; } sched = svc->scheduler; if (sched == NULL) { - IP_VS_ERR("ip_vs_unbind_scheduler(): svc isn't bound\n"); + pr_err("%s(): svc isn't bound\n", __func__); return -EINVAL; } if (sched->done_service) { if (sched->done_service(svc) != 0) { - IP_VS_ERR("ip_vs_unbind_scheduler(): done error\n"); + pr_err("%s(): done error\n", __func__); return -EINVAL; } } @@ -106,8 +106,7 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name) { struct ip_vs_scheduler *sched; - IP_VS_DBG(2, "ip_vs_sched_getbyname(): sched_name \"%s\"\n", - sched_name); + IP_VS_DBG(2, "%s(): sched_name \"%s\"\n", __func__, sched_name); read_lock_bh(&__ip_vs_sched_lock); @@ -173,12 +172,12 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) struct ip_vs_scheduler *sched; if (!scheduler) { - IP_VS_ERR("register_ip_vs_scheduler(): NULL arg\n"); + pr_err("%s(): NULL arg\n", __func__); return -EINVAL; } if (!scheduler->name) { - IP_VS_ERR("register_ip_vs_scheduler(): NULL scheduler_name\n"); + pr_err("%s(): NULL scheduler_name\n", __func__); return -EINVAL; } @@ -190,8 +189,8 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) if (!list_empty(&scheduler->n_list)) { write_unlock_bh(&__ip_vs_sched_lock); ip_vs_use_count_dec(); - IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " - "already linked\n", scheduler->name); + pr_err("%s(): [%s] scheduler already linked\n", + __func__, scheduler->name); return -EINVAL; } @@ -203,9 +202,8 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) if (strcmp(scheduler->name, sched->name) == 0) { write_unlock_bh(&__ip_vs_sched_lock); ip_vs_use_count_dec(); - IP_VS_ERR("register_ip_vs_scheduler(): [%s] scheduler " - "already existed in the system\n", - scheduler->name); + pr_err("%s(): [%s] scheduler already existed " + "in the system\n", __func__, scheduler->name); return -EINVAL; } } @@ -215,7 +213,7 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) list_add(&scheduler->n_list, &ip_vs_schedulers); write_unlock_bh(&__ip_vs_sched_lock); - IP_VS_INFO("[%s] scheduler registered.\n", scheduler->name); + pr_info("[%s] scheduler registered.\n", scheduler->name); return 0; } @@ -227,15 +225,15 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) { if (!scheduler) { - IP_VS_ERR( "unregister_ip_vs_scheduler(): NULL arg\n"); + pr_err("%s(): NULL arg\n", __func__); return -EINVAL; } write_lock_bh(&__ip_vs_sched_lock); if (list_empty(&scheduler->n_list)) { write_unlock_bh(&__ip_vs_sched_lock); - IP_VS_ERR("unregister_ip_vs_scheduler(): [%s] scheduler " - "is not in the list. failed\n", scheduler->name); + pr_err("%s(): [%s] scheduler is not in the list. failed\n", + __func__, scheduler->name); return -EINVAL; } @@ -248,7 +246,7 @@ int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler) /* decrease the module use count */ ip_vs_use_count_dec(); - IP_VS_INFO("[%s] scheduler unregistered.\n", scheduler->name); + pr_info("[%s] scheduler unregistered.\n", scheduler->name); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 4f745dd..1ab75a9 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -64,7 +64,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) struct ip_vs_dest *dest, *least; unsigned int loh, doh; - IP_VS_DBG(6, "ip_vs_sed_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* * We calculate the load of each dest server as follows: diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index fb4d2d2..8e6cfd3 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -147,7 +147,7 @@ static int ip_vs_sh_init_svc(struct ip_vs_service *svc) tbl = kmalloc(sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE, GFP_ATOMIC); if (tbl == NULL) { - IP_VS_ERR("ip_vs_sh_init_svc(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return -ENOMEM; } svc->sched_data = tbl; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index cc04c99..e177f0d 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -246,7 +246,7 @@ void ip_vs_sync_conn(struct ip_vs_conn *cp) if (!curr_sb) { if (!(curr_sb=ip_vs_sync_buff_create())) { spin_unlock(&curr_sb_lock); - IP_VS_ERR("ip_vs_sync_buff_create failed.\n"); + pr_err("ip_vs_sync_buff_create failed.\n"); return; } } @@ -412,7 +412,7 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) if (dest) atomic_dec(&dest->refcnt); if (!cp) { - IP_VS_ERR("ip_vs_conn_new failed\n"); + pr_err("ip_vs_conn_new failed\n"); return; } } else if (!cp->dest) { @@ -580,8 +580,8 @@ static int bind_mcastif_addr(struct socket *sock, char *ifname) addr = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); if (!addr) - IP_VS_ERR("You probably need to specify IP address on " - "multicast interface.\n"); + pr_err("You probably need to specify IP address on " + "multicast interface.\n"); IP_VS_DBG(7, "binding socket with (%s) %pI4\n", ifname, &addr); @@ -605,13 +605,13 @@ static struct socket * make_send_sock(void) /* First create a socket */ result = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (result < 0) { - IP_VS_ERR("Error during creation of socket; terminating\n"); + pr_err("Error during creation of socket; terminating\n"); return ERR_PTR(result); } result = set_mcast_if(sock->sk, ip_vs_master_mcast_ifn); if (result < 0) { - IP_VS_ERR("Error setting outbound mcast interface\n"); + pr_err("Error setting outbound mcast interface\n"); goto error; } @@ -620,14 +620,14 @@ static struct socket * make_send_sock(void) result = bind_mcastif_addr(sock, ip_vs_master_mcast_ifn); if (result < 0) { - IP_VS_ERR("Error binding address of the mcast interface\n"); + pr_err("Error binding address of the mcast interface\n"); goto error; } result = sock->ops->connect(sock, (struct sockaddr *) &mcast_addr, sizeof(struct sockaddr), 0); if (result < 0) { - IP_VS_ERR("Error connecting to the multicast addr\n"); + pr_err("Error connecting to the multicast addr\n"); goto error; } @@ -650,7 +650,7 @@ static struct socket * make_receive_sock(void) /* First create a socket */ result = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (result < 0) { - IP_VS_ERR("Error during creation of socket; terminating\n"); + pr_err("Error during creation of socket; terminating\n"); return ERR_PTR(result); } @@ -660,7 +660,7 @@ static struct socket * make_receive_sock(void) result = sock->ops->bind(sock, (struct sockaddr *) &mcast_addr, sizeof(struct sockaddr)); if (result < 0) { - IP_VS_ERR("Error binding to the multicast addr\n"); + pr_err("Error binding to the multicast addr\n"); goto error; } @@ -669,7 +669,7 @@ static struct socket * make_receive_sock(void) (struct in_addr *) &mcast_addr.sin_addr, ip_vs_backup_mcast_ifn); if (result < 0) { - IP_VS_ERR("Error joining to the multicast group\n"); + pr_err("Error joining to the multicast group\n"); goto error; } @@ -709,7 +709,7 @@ ip_vs_send_sync_msg(struct socket *sock, struct ip_vs_sync_mesg *msg) msg->size = htons(msg->size); if (ip_vs_send_async(sock, (char *)msg, msize) != msize) - IP_VS_ERR("ip_vs_send_async error\n"); + pr_err("ip_vs_send_async error\n"); } static int @@ -740,9 +740,9 @@ static int sync_thread_master(void *data) struct ip_vs_sync_thread_data *tinfo = data; struct ip_vs_sync_buff *sb; - IP_VS_INFO("sync thread started: state = MASTER, mcast_ifn = %s, " - "syncid = %d\n", - ip_vs_master_mcast_ifn, ip_vs_master_syncid); + pr_info("sync thread started: state = MASTER, mcast_ifn = %s, " + "syncid = %d\n", + ip_vs_master_mcast_ifn, ip_vs_master_syncid); while (!kthread_should_stop()) { while ((sb = sb_dequeue())) { @@ -783,9 +783,9 @@ static int sync_thread_backup(void *data) struct ip_vs_sync_thread_data *tinfo = data; int len; - IP_VS_INFO("sync thread started: state = BACKUP, mcast_ifn = %s, " - "syncid = %d\n", - ip_vs_backup_mcast_ifn, ip_vs_backup_syncid); + pr_info("sync thread started: state = BACKUP, mcast_ifn = %s, " + "syncid = %d\n", + ip_vs_backup_mcast_ifn, ip_vs_backup_syncid); while (!kthread_should_stop()) { wait_event_interruptible(*tinfo->sock->sk->sk_sleep, @@ -797,7 +797,7 @@ static int sync_thread_backup(void *data) len = ip_vs_receive(tinfo->sock, tinfo->buf, sync_recv_mesg_maxlen); if (len <= 0) { - IP_VS_ERR("receiving message error\n"); + pr_err("receiving message error\n"); break; } @@ -827,7 +827,7 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) int (*threadfn)(void *data); int result = -ENOMEM; - IP_VS_DBG(7, "%s: pid %d\n", __func__, task_pid_nr(current)); + IP_VS_DBG(7, "%s(): pid %d\n", __func__, task_pid_nr(current)); IP_VS_DBG(7, "Each ip_vs_sync_conn entry needs %Zd bytes\n", sizeof(struct ip_vs_sync_conn)); @@ -904,14 +904,14 @@ out: int stop_sync_thread(int state) { - IP_VS_DBG(7, "%s: pid %d\n", __func__, task_pid_nr(current)); + IP_VS_DBG(7, "%s(): pid %d\n", __func__, task_pid_nr(current)); if (state == IP_VS_STATE_MASTER) { if (!sync_master_thread) return -ESRCH; - IP_VS_INFO("stopping master sync thread %d ...\n", - task_pid_nr(sync_master_thread)); + pr_info("stopping master sync thread %d ...\n", + task_pid_nr(sync_master_thread)); /* * The lock synchronizes with sb_queue_tail(), so that we don't @@ -928,8 +928,8 @@ int stop_sync_thread(int state) if (!sync_backup_thread) return -ESRCH; - IP_VS_INFO("stopping backup sync thread %d ...\n", - task_pid_nr(sync_backup_thread)); + pr_info("stopping backup sync thread %d ...\n", + task_pid_nr(sync_backup_thread)); ip_vs_sync_state &= ~IP_VS_STATE_BACKUP; kthread_stop(sync_backup_thread); diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index c39ebb6..70ff82cd 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -97,7 +97,7 @@ static int ip_vs_wrr_init_svc(struct ip_vs_service *svc) */ mark = kmalloc(sizeof(struct ip_vs_wrr_mark), GFP_ATOMIC); if (mark == NULL) { - IP_VS_ERR("ip_vs_wrr_init_svc(): no memory\n"); + pr_err("%s(): no memory\n", __func__); return -ENOMEM; } mark->cl = &svc->destinations; @@ -144,7 +144,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) struct ip_vs_wrr_mark *mark = svc->sched_data; struct list_head *p; - IP_VS_DBG(6, "ip_vs_wrr_schedule(): Scheduling...\n"); + IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); /* * This loop will always terminate, because mark->cw in (0, max_weight] diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 061e76d..30b3189 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -238,8 +238,8 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (ip_route_output_key(&init_net, &rt, &fl)) { - IP_VS_DBG_RL("ip_vs_bypass_xmit(): ip_route_output error, dest: %pI4\n", - &iph->daddr); + IP_VS_DBG_RL("%s(): ip_route_output error, dest: %pI4\n", + __func__, &iph->daddr); goto tx_error_icmp; } @@ -248,7 +248,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if ((skb->len > mtu) && (iph->frag_off & htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); - IP_VS_DBG_RL("ip_vs_bypass_xmit(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -302,8 +302,8 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl); if (!rt) { - IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): ip6_route_output error, dest: %pI6\n", - &iph->daddr); + IP_VS_DBG_RL("%s(): ip6_route_output error, dest: %pI6\n", + __func__, &iph->daddr); goto tx_error_icmp; } @@ -312,7 +312,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb->len > mtu) { dst_release(&rt->u.dst); icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); - IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -539,9 +539,9 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (skb->protocol != htons(ETH_P_IP)) { - IP_VS_DBG_RL("ip_vs_tunnel_xmit(): protocol error, " + IP_VS_DBG_RL("%s(): protocol error, " "ETH_P_IP: %d, skb protocol: %d\n", - htons(ETH_P_IP), skb->protocol); + __func__, htons(ETH_P_IP), skb->protocol); goto tx_error; } @@ -553,7 +553,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, mtu = dst_mtu(&rt->u.dst) - sizeof(struct iphdr); if (mtu < 68) { ip_rt_put(rt); - IP_VS_DBG_RL("ip_vs_tunnel_xmit(): mtu less than 68\n"); + IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__); goto tx_error; } if (skb_dst(skb)) @@ -565,7 +565,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, && mtu < ntohs(old_iph->tot_len)) { icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); - IP_VS_DBG_RL("ip_vs_tunnel_xmit(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -581,7 +581,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (!new_skb) { ip_rt_put(rt); kfree_skb(skb); - IP_VS_ERR_RL("ip_vs_tunnel_xmit(): no memory\n"); + IP_VS_ERR_RL("%s(): no memory\n", __func__); return NF_STOLEN; } kfree_skb(skb); @@ -649,9 +649,9 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (skb->protocol != htons(ETH_P_IPV6)) { - IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): protocol error, " + IP_VS_DBG_RL("%s(): protocol error, " "ETH_P_IPV6: %d, skb protocol: %d\n", - htons(ETH_P_IPV6), skb->protocol); + __func__, htons(ETH_P_IPV6), skb->protocol); goto tx_error; } @@ -665,7 +665,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, /* TODO IPv6: do we need this check in IPv6? */ if (mtu < 1280) { dst_release(&rt->u.dst); - IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): mtu less than 1280\n"); + IP_VS_DBG_RL("%s(): mtu less than 1280\n", __func__); goto tx_error; } if (skb_dst(skb)) @@ -674,7 +674,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (mtu < ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr)) { icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); dst_release(&rt->u.dst); - IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -690,7 +690,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (!new_skb) { dst_release(&rt->u.dst); kfree_skb(skb); - IP_VS_ERR_RL("ip_vs_tunnel_xmit_v6(): no memory\n"); + IP_VS_ERR_RL("%s(): no memory\n", __func__); return NF_STOLEN; } kfree_skb(skb); @@ -763,7 +763,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if ((iph->frag_off & htons(IP_DF)) && skb->len > mtu) { icmp_send(skb, ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); - IP_VS_DBG_RL("ip_vs_dr_xmit(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -816,7 +816,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb->len > mtu) { icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); dst_release(&rt->u.dst); - IP_VS_DBG_RL("ip_vs_dr_xmit_v6(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -891,7 +891,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if ((skb->len > mtu) && (ip_hdr(skb)->frag_off & htons(IP_DF))) { ip_rt_put(rt); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); - IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } @@ -966,7 +966,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb->len > mtu) { dst_release(&rt->u.dst); icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); - IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n"); + IP_VS_DBG_RL("%s(): frag needed\n", __func__); goto tx_error; } -- cgit v1.1 From e4c4e448cf557921ffbbbd6d6ddac81fdceacb4f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 30 Jul 2009 03:15:07 +0000 Subject: neigh: Convert garbage collection from softirq to workqueue Current neigh_periodic_timer() function is fired by timer IRQ, and scans one hash bucket each round (very litle work in fact) As we are supposed to scan whole hash table in 15 seconds, this means neigh_periodic_timer() can be fired very often. (depending on the number of concurrent hash entries we stored in this table) Converting this to a workqueue permits scanning whole table, minimizing icache pollution, and firing this work every 15 seconds, independantly of hash table size. This 15 seconds delay is not a hard number, as work is a deferrable one. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/neighbour.c | 89 +++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 46 deletions(-) (limited to 'net') diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c6f9ad8..e587e68 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -692,75 +692,74 @@ static void neigh_connect(struct neighbour *neigh) hh->hh_output = neigh->ops->hh_output; } -static void neigh_periodic_timer(unsigned long arg) +static void neigh_periodic_work(struct work_struct *work) { - struct neigh_table *tbl = (struct neigh_table *)arg; + struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work); struct neighbour *n, **np; - unsigned long expire, now = jiffies; + unsigned int i; NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); - write_lock(&tbl->lock); + write_lock_bh(&tbl->lock); /* * periodically recompute ReachableTime from random function */ - if (time_after(now, tbl->last_rand + 300 * HZ)) { + if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { struct neigh_parms *p; - tbl->last_rand = now; + tbl->last_rand = jiffies; for (p = &tbl->parms; p; p = p->next) p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); } - np = &tbl->hash_buckets[tbl->hash_chain_gc]; - tbl->hash_chain_gc = ((tbl->hash_chain_gc + 1) & tbl->hash_mask); + for (i = 0 ; i <= tbl->hash_mask; i++) { + np = &tbl->hash_buckets[i]; - while ((n = *np) != NULL) { - unsigned int state; + while ((n = *np) != NULL) { + unsigned int state; - write_lock(&n->lock); + write_lock(&n->lock); - state = n->nud_state; - if (state & (NUD_PERMANENT | NUD_IN_TIMER)) { - write_unlock(&n->lock); - goto next_elt; - } + state = n->nud_state; + if (state & (NUD_PERMANENT | NUD_IN_TIMER)) { + write_unlock(&n->lock); + goto next_elt; + } - if (time_before(n->used, n->confirmed)) - n->used = n->confirmed; + if (time_before(n->used, n->confirmed)) + n->used = n->confirmed; - if (atomic_read(&n->refcnt) == 1 && - (state == NUD_FAILED || - time_after(now, n->used + n->parms->gc_staletime))) { - *np = n->next; - n->dead = 1; + if (atomic_read(&n->refcnt) == 1 && + (state == NUD_FAILED || + time_after(jiffies, n->used + n->parms->gc_staletime))) { + *np = n->next; + n->dead = 1; + write_unlock(&n->lock); + neigh_cleanup_and_release(n); + continue; + } write_unlock(&n->lock); - neigh_cleanup_and_release(n); - continue; - } - write_unlock(&n->lock); next_elt: - np = &n->next; + np = &n->next; + } + /* + * It's fine to release lock here, even if hash table + * grows while we are preempted. + */ + write_unlock_bh(&tbl->lock); + cond_resched(); + write_lock_bh(&tbl->lock); } - /* Cycle through all hash buckets every base_reachable_time/2 ticks. * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 * base_reachable_time. */ - expire = tbl->parms.base_reachable_time >> 1; - expire /= (tbl->hash_mask + 1); - if (!expire) - expire = 1; - - if (expire>HZ) - mod_timer(&tbl->gc_timer, round_jiffies(now + expire)); - else - mod_timer(&tbl->gc_timer, now + expire); - - write_unlock(&tbl->lock); + schedule_delayed_work(&tbl->gc_work, + tbl->parms.base_reachable_time >> 1); + write_unlock_bh(&tbl->lock); } static __inline__ int neigh_max_probes(struct neighbour *n) @@ -1442,10 +1441,8 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl) get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd)); rwlock_init(&tbl->lock); - setup_timer(&tbl->gc_timer, neigh_periodic_timer, (unsigned long)tbl); - tbl->gc_timer.expires = now + 1; - add_timer(&tbl->gc_timer); - + INIT_DELAYED_WORK_DEFERRABLE(&tbl->gc_work, neigh_periodic_work); + schedule_delayed_work(&tbl->gc_work, tbl->parms.reachable_time); setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl); skb_queue_head_init_class(&tbl->proxy_queue, &neigh_table_proxy_queue_class); @@ -1482,7 +1479,8 @@ int neigh_table_clear(struct neigh_table *tbl) struct neigh_table **tp; /* It is not clean... Fix it to unload IPv6 module safely */ - del_timer_sync(&tbl->gc_timer); + cancel_delayed_work(&tbl->gc_work); + flush_scheduled_work(); del_timer_sync(&tbl->proxy_timer); pneigh_queue_purge(&tbl->proxy_queue); neigh_ifdown(tbl, NULL); @@ -1752,7 +1750,6 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, .ndtc_last_rand = jiffies_to_msecs(rand_delta), .ndtc_hash_rnd = tbl->hash_rnd, .ndtc_hash_mask = tbl->hash_mask, - .ndtc_hash_chain_gc = tbl->hash_chain_gc, .ndtc_proxy_qlen = tbl->proxy_queue.qlen, }; -- cgit v1.1 From 4da163ab0a224590f3cae67c1d54ae8c428f6223 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Jul 2009 20:28:40 +0200 Subject: mac80211: disable software retry for now Pavel Roskin reported a problem that seems to be due to software retry of already transmitted frames. It turns out that we've never done that correctly, but due to some recent changes it now crashes in the TX code. I've added a comment in the patch that explains the problem better and also points to possible solutions -- which I can't implement right now. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c1a7991..9dd8d25 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -310,6 +310,31 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + /* + * XXX: This is temporary! + * + * The problem here is that when we get here, the driver will + * quite likely have pretty much overwritten info->control by + * using info->driver_data or info->rate_driver_data. Thus, + * when passing out the frame to the driver again, we would be + * passing completely bogus data since the driver would then + * expect a properly filled info->control. In mac80211 itself + * the same problem occurs, since we need info->control.vif + * internally. + * + * To fix this, we should send the frame through TX processing + * again. However, it's not that simple, since the frame will + * have been software-encrypted (if applicable) already, and + * encrypting it again doesn't do much good. So to properly do + * that, we not only have to skip the actual 'raw' encryption + * (key selection etc. still has to be done!) but also the + * sequence number assignment since that impacts the crypto + * encapsulation, of course. + * + * Hence, for now, fix the bug by just dropping the frame. + */ + goto drop; + sta->tx_filtered_count++; /* @@ -363,6 +388,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, return; } + drop: #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) printk(KERN_DEBUG "%s: dropped TX filtered frame, " -- cgit v1.1 From dd21dcdc65a182222666199ec4cea188a11cb5d2 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 27 Jul 2009 16:10:22 -0700 Subject: wext: remove extra return on wireless_nlevent_init() Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/wext.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 3fe3c2c..5b4a0ce 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1291,7 +1291,6 @@ static struct pernet_operations wext_pernet_ops = { static int __init wireless_nlevent_init(void) { return register_pernet_subsys(&wext_pernet_ops); - return 0; } subsys_initcall(wireless_nlevent_init); -- cgit v1.1 From 97af743207466ff8b477e14bfb7af0ba2c93375b Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 29 Jul 2009 10:13:03 +0200 Subject: mac80211: disable beacons before removing the associated interface When downing interfaces, it's a good idea to tell the driver to stop sending beacons; that way the driver doesn't need special code in ops->remove_interface() when it should already handle the case in bss_info_changed(). This fixes a potential crash with at least ath5k since the vif pointer will be nullified while beacon interrupts are still active. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 10 ++++++++++ net/mac80211/main.c | 3 ++- net/mac80211/pm.c | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6c655b6..6614d4f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -522,6 +522,16 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_scan_completed(&local->hw, true); } + /* + * Disable beaconing for AP and mesh, IBSS can't + * still be joined to a network at this point. + */ + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_BEACON_ENABLED); + } + conf.vif = &sdata->vif; conf.type = sdata->vif.type; conf.mac_addr = dev->dev_addr; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9dd8d25..5e76dd1 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -198,7 +198,8 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, } if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (test_bit(SCAN_SW_SCANNING, &local->scanning)) { + if (local->quiescing || !netif_running(sdata->dev) || + test_bit(SCAN_SW_SCANNING, &local->scanning)) { sdata->vif.bss_conf.enable_beacon = false; } else { /* diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 5e3d476..3320f7d 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -96,6 +96,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) if (!netif_running(sdata->dev)) continue; + /* disable beaconing */ + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_BEACON_ENABLED); + conf.vif = &sdata->vif; conf.type = sdata->vif.type; conf.mac_addr = sdata->dev->dev_addr; -- cgit v1.1 From df7fc0f9735085bb617fff774bfd71465edb448c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 Jul 2009 11:23:49 +0200 Subject: cfg80211: keep track of current_bss for userspace SME When a userspace SME is active, we're currently not keeping track of the BSS properly for reporting the current link and for internal use. Additionally, it looks like there is a possible BSS leak in that the BSS never gets removed from auth_bsses[]. To fix it, pass the BSS struct to __cfg80211_connect_result in this case. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 3 ++- net/wireless/core.h | 8 +++++++- net/wireless/mlme.c | 38 ++++++++++++++++++++++++-------------- net/wireless/sme.c | 35 ++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index f9fee65..755cdf1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -314,7 +314,8 @@ static void cfg80211_process_events(struct wireless_dev *wdev) ev->cr.req_ie, ev->cr.req_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS); + ev->cr.status == WLAN_STATUS_SUCCESS, + NULL); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bssid, diff --git a/net/wireless/core.h b/net/wireless/core.h index 6d903c1..325c17e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -127,6 +127,11 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu return container_of(pub, struct cfg80211_internal_bss, pub); } +static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) +{ + kref_get(&bss->ref); +} + static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); @@ -323,7 +328,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev); + u16 status, bool wextev, + struct cfg80211_bss *bss); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 097a87d..525e8e2 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -61,7 +61,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - bool done; + struct cfg80211_internal_bss *bss = NULL; wdev_lock(wdev); @@ -69,22 +69,32 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS); - if (status_code == WLAN_STATUS_SUCCESS) { - for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] == wdev->current_bss) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i]) + continue; + if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, + ETH_ALEN) == 0) { + bss = wdev->auth_bsses[i]; wdev->auth_bsses[i] = NULL; - done = true; + /* additional reference to drop hold */ + cfg80211_ref_bss(bss); break; } } - WARN_ON(!done); + WARN_ON(!bss); + } + + /* this consumes one bss reference (unless bss is NULL) */ + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS, + bss ? &bss->pub : NULL); + /* drop hold now, and also reference acquired above */ + if (bss) { + cfg80211_unhold_bss(bss); + cfg80211_put_bss(&bss->pub); } wdev_unlock(wdev); @@ -144,7 +154,7 @@ static void __cfg80211_send_deauth(struct net_device *dev, } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); } } @@ -241,7 +251,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] && @@ -275,7 +285,7 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { if (wdev->auth_bsses[i] && diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d2b5d4c..3728d2b 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -182,7 +182,7 @@ void cfg80211_conn_work(struct work_struct *work) wdev->conn->params.bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); wdev_unlock(wdev); } @@ -247,7 +247,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) wdev->conn->params.bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - false); + false, NULL); } } @@ -305,7 +305,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev, schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - status_code, false); + status_code, false, NULL); } else if (wdev->sme_state == CFG80211_SME_CONNECTING && wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; @@ -316,10 +316,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev) + u16 status, bool wextev, + struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -361,6 +361,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } #endif + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + if (status == WLAN_STATUS_SUCCESS && wdev->sme_state == CFG80211_SME_IDLE) goto success; @@ -368,12 +374,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->sme_state != CFG80211_SME_CONNECTING) return; - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; @@ -386,10 +386,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; } - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); + success: + if (!bss) + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; @@ -397,7 +399,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); - success: wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); } @@ -788,7 +789,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, else if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev); + wextev, NULL); return 0; } -- cgit v1.1 From 57c9fff3d0c5d77bc7910717faeca4e8a018b1ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 Jul 2009 15:46:21 +0200 Subject: mac80211: fix sparse warnings/errors sparse complains about a shadowed variable, which we can just rename, and lots of stuff if the API tracer is enabled, so kick out the tracer code in a sparse run -- the macros just confuse it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/driver-trace.c | 3 +++ net/mac80211/iface.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-trace.c b/net/mac80211/driver-trace.c index 6da6f79..8ed8711 100644 --- a/net/mac80211/driver-trace.c +++ b/net/mac80211/driver-trace.c @@ -1,6 +1,9 @@ /* bug in tracepoint.h, it should include this */ #include +/* sparse isn't too happy with all macros... */ +#ifndef __CHECKER__ #include "driver-ops.h" #define CREATE_TRACE_POINTS #include "driver-trace.h" +#endif diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6614d4f..a83087f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -401,7 +401,7 @@ static int ieee80211_stop(struct net_device *dev) /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { - struct ieee80211_sub_if_data *vlan, *tmp; + struct ieee80211_sub_if_data *vlan, *tmpsdata; struct beacon_data *old_beacon = sdata->u.ap.beacon; /* remove beacon */ @@ -410,7 +410,7 @@ static int ieee80211_stop(struct net_device *dev) kfree(old_beacon); /* down all dependent devices, that is VLANs */ - list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans, + list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); -- cgit v1.1 From 42935ecaf4e784d0815afa9a7e5fe7e141157ca3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 29 Jul 2009 20:08:07 -0400 Subject: mac80211: redefine usage of the mac80211 workqueue The mac80211 workqueue exists to enable mac80211 and drivers to queue their own work on a single threaded workqueue. mac80211 takes care to flush the workqueue during suspend but we never really had requirements on drivers for how they should use the workqueue in consideration for suspend. We extend mac80211 to document how the mac80211 workqueue should be used, how it should not be used and finally move raw access to the workqueue to mac80211 only. Drivers and mac80211 use helpers to queue work onto the mac80211 workqueue: * ieee80211_queue_work() * ieee80211_queue_delayed_work() These helpers will now warn if mac80211 already completed its suspend cycle and someone is trying to queue work. mac80211 flushes the mac80211 workqueue prior to suspend a few times, but we haven't taken the care to ensure drivers won't add more work after suspend. To help with this we add a warning when someone tries to add work and mac80211 already completed the suspend cycle. Drivers should ensure they cancel any work or delayed work in the mac80211 stop() callback. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 6 +++--- net/mac80211/ieee80211_i.h | 6 ++++++ net/mac80211/iface.c | 4 ++-- net/mac80211/main.c | 8 ++++---- net/mac80211/mesh.c | 10 +++++----- net/mac80211/mesh_hwmp.c | 4 ++-- net/mac80211/mlme.c | 48 +++++++++++++++++++++------------------------- net/mac80211/pm.c | 4 ++-- net/mac80211/scan.c | 8 ++++---- net/mac80211/tx.c | 2 +- net/mac80211/util.c | 41 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 92 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6e3cca6..920ec87 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -781,7 +781,7 @@ static void ieee80211_ibss_timer(unsigned long data) } set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); - queue_work(local->hw.workqueue, &ifibss->work); + ieee80211_queue_work(&local->hw, &ifibss->work); } #ifdef CONFIG_PM @@ -853,7 +853,7 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_AUTH: skb_queue_tail(&sdata->u.ibss.skb_queue, skb); - queue_work(local->hw.workqueue, &sdata->u.ibss.work); + ieee80211_queue_work(&local->hw, &sdata->u.ibss.work); return RX_QUEUED; } @@ -912,7 +912,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_idle(sdata->local); set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); - queue_work(sdata->local->hw.workqueue, &sdata->u.ibss.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.ibss.work); return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index aec6853..316825b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -614,6 +614,12 @@ struct ieee80211_local { const struct ieee80211_ops *ops; + /* + * private workqueue to mac80211. mac80211 makes this accessible + * via ieee80211_queue_work() + */ + struct workqueue_struct *workqueue; + 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/iface.c b/net/mac80211/iface.c index a83087f..8c1284d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -312,7 +312,7 @@ static int ieee80211_open(struct net_device *dev) * to fix this. */ if (sdata->vif.type == NL80211_IFTYPE_STATION) - queue_work(local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); netif_tx_start_all_queues(dev); @@ -551,7 +551,7 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_led_radio(local, false); - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); tasklet_disable(&local->tx_pending_tasklet); tasklet_disable(&local->tasklet); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5e76dd1..22e0738 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -821,9 +821,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - local->hw.workqueue = + local->workqueue = create_singlethread_workqueue(wiphy_name(local->hw.wiphy)); - if (!local->hw.workqueue) { + if (!local->workqueue) { result = -ENOMEM; goto fail_workqueue; } @@ -913,7 +913,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sta_info_stop(local); fail_sta_info: debugfs_hw_del(local); - destroy_workqueue(local->hw.workqueue); + destroy_workqueue(local->workqueue); fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: @@ -955,7 +955,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue_unreliable); - destroy_workqueue(local->hw.workqueue); + destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); ieee80211_wep_free(local); ieee80211_led_exit(local); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 9a38269..2f4f518 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -54,7 +54,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); } /** @@ -357,7 +357,7 @@ static void ieee80211_mesh_path_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); } struct mesh_table *mesh_table_grow(struct mesh_table *tbl) @@ -471,7 +471,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; ifmsh->housekeeping = true; - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED); } @@ -619,7 +619,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) if (ieee80211_vif_is_mesh(&sdata->vif)) - queue_work(local->hw.workqueue, &sdata->u.mesh.work); + ieee80211_queue_work(local->hw.workqueue, &sdata->u.mesh.work); rcu_read_unlock(); } @@ -692,7 +692,7 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: skb_queue_tail(&ifmsh->skb_queue, skb); - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); return RX_QUEUED; } diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index e93c37e..11ab71a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -660,14 +660,14 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) spin_unlock(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) - queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); else if (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around */ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1; - queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); } else mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq + min_preq_int_jiff(sdata)); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ee83125..0779ba1 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -565,7 +565,7 @@ static void ieee80211_chswitch_timer(unsigned long data) return; } - queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); + ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, @@ -597,7 +597,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->local->csa_channel = new_ch; if (sw_elem->count <= 1) { - queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); + ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } else { ieee80211_stop_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -763,7 +763,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data) if (local->quiescing || local->suspended) return; - queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); + ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } /* MLME */ @@ -950,7 +950,7 @@ ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_AUTH_TO; } @@ -995,7 +995,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_AUTH_TO; } @@ -1124,7 +1124,7 @@ ieee80211_associate(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_ASSOC_TO; } @@ -1232,8 +1232,7 @@ 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); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); @@ -1888,7 +1887,7 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_DISASSOC: case IEEE80211_STYPE_ACTION: skb_queue_tail(&sdata->u.mgd.skb_queue, skb); - queue_work(local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); return RX_QUEUED; } @@ -2026,7 +2025,7 @@ static void ieee80211_sta_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); } static void ieee80211_sta_work(struct work_struct *work) @@ -2051,13 +2050,11 @@ static void ieee80211_sta_work(struct work_struct *work) return; /* - * Nothing should have been stuffed into the workqueue during - * the suspend->resume cycle. If this WARN is seen then there - * is a bug with either the driver suspend or something in - * mac80211 stuffing into the workqueue which we haven't yet - * cleared during mac80211's suspend cycle. + * ieee80211_queue_work() should have picked up most cases, + * here we'll pick the the rest. */ - if (WARN_ON(local->suspended)) + if (WARN(local->suspended, "STA MLME work scheduled while " + "going to suspend\n")) return; ifmgd = &sdata->u.mgd; @@ -2113,9 +2110,9 @@ static void ieee80211_sta_work(struct work_struct *work) mutex_unlock(&ifmgd->mtx); if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) - queue_delayed_work(local->hw.workqueue, - &local->scan_work, - round_jiffies_relative(0)); + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + round_jiffies_relative(0)); return; } @@ -2196,8 +2193,7 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; - queue_work(sdata->local->hw.workqueue, - &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) @@ -2210,7 +2206,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) if (local->quiescing) return; - queue_work(local->hw.workqueue, &ifmgd->monitor_work); + ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); } static void ieee80211_sta_monitor_work(struct work_struct *work) @@ -2229,10 +2225,10 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) IEEE80211_STA_CONNECTION_POLL); /* let's probe the connection once */ - queue_work(sdata->local->hw.workqueue, + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work); /* and do all the other regular work too */ - queue_work(sdata->local->hw.workqueue, + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); } } @@ -2393,7 +2389,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, list_add(&wk->list, &sdata->u.mgd.work_list); mutex_unlock(&ifmgd->mtx); - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); return 0; } @@ -2467,7 +2463,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, else ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); err = 0; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 3320f7d..a5d2f1f 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -26,7 +26,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) /* make quiescing visible to timers everywhere */ mb(); - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); /* Don't try to run timers while suspended. */ del_timer_sync(&local->sta_cleanup); @@ -117,7 +117,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) * shouldn't be doing (or cancel everything in the * stop callback) that but better safe than sorry. */ - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); local->suspended = true; /* need suspended to be visible before quiescing is false */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4573100..244f53f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -385,8 +385,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) spin_unlock_bh(&local->filter_lock); /* TODO: start scan as soon as all nullfunc frames are ACKed */ - queue_delayed_work(local->hw.workqueue, &local->scan_work, - IEEE80211_CHANNEL_TIME); + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + IEEE80211_CHANNEL_TIME); return 0; } @@ -715,8 +716,7 @@ void ieee80211_scan_work(struct work_struct *work) } } while (next_delay == 0); - queue_delayed_work(local->hw.workqueue, &local->scan_work, - next_delay); + ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); } int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4e1b2ba..7cffaa0 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1400,7 +1400,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); - queue_work(local->hw.workqueue, + ieee80211_queue_work(&local->hw, &local->dynamic_ps_disable_work); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8502936..e55d57f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -511,6 +511,46 @@ void ieee80211_iterate_active_interfaces_atomic( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); +/* + * Nothing should have been stuffed into the workqueue during + * the suspend->resume cycle. If this WARN is seen then there + * is a bug with either the driver suspend or something in + * mac80211 stuffing into the workqueue which we haven't yet + * cleared during mac80211's suspend cycle. + */ +static bool ieee80211_can_queue_work(struct ieee80211_local *local) +{ + if (WARN(local->suspended, "queueing ieee80211 work while " + "going to suspend\n")) + return false; + + return true; +} + +void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (!ieee80211_can_queue_work(local)) + return; + + queue_work(local->workqueue, work); +} +EXPORT_SYMBOL(ieee80211_queue_work); + +void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, + struct delayed_work *dwork, + unsigned long delay) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (!ieee80211_can_queue_work(local)) + return; + + queue_delayed_work(local->workqueue, dwork, delay); +} +EXPORT_SYMBOL(ieee80211_queue_delayed_work); + void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems) { @@ -1114,3 +1154,4 @@ int ieee80211_reconfig(struct ieee80211_local *local) #endif return 0; } + -- cgit v1.1 From e40cbdac0629402a4cb0c3bca0cc19ab7a00e00d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 30 Jul 2009 14:04:01 +0200 Subject: cfg80211: fix NETDEV_UNREGISTER notifier It's possible to get the NETDEV_UNREGISTER callback multiple times (see net/core/dev.c:netdev_wait_allrefs) and this will completely mess up our cleanup code. To avoid that, clean up only when the interface is still on the wiphy interface list from which it's removed on the first NETDEV_UNREGISTER call. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 755cdf1..1493285 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -725,15 +725,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NETDEV_UNREGISTER: mutex_lock(&rdev->devlist_mtx); + /* + * It is possible to get NETDEV_UNREGISTER + * multiple times. To detect that, check + * that the interface is still on the list + * of registered interfaces, and only then + * remove and clean it up. + */ if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); - } - mutex_unlock(&rdev->devlist_mtx); - mutex_destroy(&wdev->mtx); + mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT - kfree(wdev->wext.keys); + kfree(wdev->wext.keys); #endif + } + mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) -- cgit v1.1 From 056508dcb6890586745aa937e779f00fde05531b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 30 Jul 2009 21:43:55 +0200 Subject: mac80211: fix powersave Some of the recent MLME rework I did broke powersave because the ps_sdata isn't assigned at the right time, and the work item wasn't removed from the list before calling ieee80211_recalc_ps(). To be more specific, this broke the case where you'd enabled PS before associating, either automatically or with iwconfig. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0779ba1..2d5edfd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -916,12 +916,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, bss_info_changed); - /* will be same as sdata */ - if (local->ps_sdata) { - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); - } + mutex_lock(&local->iflist_mtx); + ieee80211_recalc_ps(local, -1); + mutex_unlock(&local->iflist_mtx); netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); @@ -1569,6 +1566,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, wk->bss->cbss.bssid, ap_ht_cap_flags); + /* delete work item -- must be before set_associated for PS */ + list_del(&wk->list); + /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; @@ -1582,7 +1582,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); mod_beacon_timer(sdata); - list_del(&wk->list); kfree(wk); return RX_MGMT_CFG80211_ASSOC; } -- cgit v1.1 From 9828b0170eef541c3fa583caa0134dadbb1d3ea6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Jul 2009 17:38:06 -0700 Subject: cfg80211: use goto out on country IE reg hint failure This has no functional changes. Acked-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index fb40428..2d1d183 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1774,10 +1774,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, mutex_lock(&cfg80211_mutex); - if (unlikely(!last_request)) { - mutex_unlock(&cfg80211_mutex); - return; - } + if (unlikely(!last_request)) + goto out; /* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) -- cgit v1.1 From 4b44c8bc4d077f1a7a9e5e946a1400c3cbcadee7 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Jul 2009 17:38:07 -0700 Subject: cfg80211: do not iterate over rdev list on country IE hint Simplify the country IE hint code by just bailing out if a previous country IE has been issued. We currently just trust the first AP we connect to on any card. The idea was to perform conflict resolution within this routine but since we can no longer iterate over the registered device list here we leave conflict resolution to be dealt with at a later time on the workqueue. This code has no functional changes other than saving us an interation over the registered device list when a second card is connected, or you unplug and connect the same one, and a country IE is received. This would have been done upon every beacon received. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 51 +++++++++------------------------------------------ 1 file changed, 9 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2d1d183..f386981 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1762,6 +1762,10 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, return false; } +/* + * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and + * therefore cannot iterate over the rdev list here. + */ void regulatory_hint_11d(struct wiphy *wiphy, u8 *country_ie, u8 country_ie_len) @@ -1804,51 +1808,14 @@ 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 + * + * We leave conflict resolution to the workqueue, where can hold + * cfg80211_mutex. */ if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) { - struct cfg80211_registered_device *rdev_last_ie; - - rdev_last_ie = - cfg80211_rdev_by_wiphy_idx(last_request->wiphy_idx); - - /* - * Lets keep this simple -- we trust the first AP - * after we intersect with CRDA - */ - if (likely(&rdev_last_ie->wiphy == wiphy)) { - /* - * Ignore IEs coming in on this wiphy with - * the same alpha2 and environment cap - */ - if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, - alpha2) && - env == rdev_last_ie->env)) { - goto out; - } - /* - * the wiphy moved on to another BSSID or the AP - * was reconfigured. XXX: We need to deal with the - * case where the user suspends and goes to goes - * to another country, and then gets IEs from an - * AP with different settings - */ - goto out; - } else { - /* - * Ignore IEs coming in on two separate wiphys with - * the same alpha2 and environment cap - */ - if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, - alpha2) && - env == rdev_last_ie->env)) { - goto out; - } - /* We could potentially intersect though */ - goto out; - } - } + wiphy_idx_valid(last_request->wiphy_idx))) + goto out; rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); if (!rd) -- cgit v1.1 From abc7381bcca6ce9dc101f112a13e14957bfbda7e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Jul 2009 17:38:08 -0700 Subject: cfg80211: decouple regulatory variables from cfg80211_mutex We change regulatory code to be protected by its own regulatory mutex and alleviate cfg80211_mutex to only be used to protect cfg80211_rdev_list, the registered device list. By doing this we will be able to work on regulatory core components without having to have hog up the cfg80211_mutex. An example here is we no longer need to use the cfg80211_mutex during driver specific wiphy_apply_custom_regulatory(). We also no longer need it for the the country IE regulatory hint; by doing so we end up curing this new lockdep warning: ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.31-rc4-wl #12 ------------------------------------------------------- phy1/1709 is trying to acquire lock: (cfg80211_mutex){+.+.+.}, at: [] regulatory_hint_11d+0x32/0x3f0 [cfg80211] but task is already holding lock: (&ifmgd->mtx){+.+.+.}, at: [] ieee80211_sta_work+0x108/0x10f0 [mac80211] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #3 (&ifmgd->mtx){+.+.+.}: [] __lock_acquire+0xd76/0x12b0 [] lock_acquire+0xe3/0x120 [] mutex_lock_nested+0x44/0x350 [] ieee80211_mgd_auth+0x108/0x1f0 [mac80211] [] ieee80211_auth+0x13/0x20 [mac80211] [] __cfg80211_mlme_auth+0x1b1/0x2a0 [cfg80211] [] cfg80211_mlme_auth+0x86/0xc0 [cfg80211] [] nl80211_authenticate+0x21d/0x230 [cfg80211] [] genl_rcv_msg+0x1b6/0x1f0 [] netlink_rcv_skb+0x89/0xb0 [] genl_rcv+0x29/0x40 [] netlink_unicast+0x29d/0x2b0 [] netlink_sendmsg+0x214/0x300 [] sock_sendmsg+0x107/0x130 [] sys_sendmsg+0x189/0x320 [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff -> #2 (&wdev->mtx){+.+.+.}: [] __lock_acquire+0xd76/0x12b0 [] lock_acquire+0xe3/0x120 [] mutex_lock_nested+0x44/0x350 [] cfg80211_netdev_notifier_call+0x1a4/0x390 [cfg80211] [] notifier_call_chain+0x3f/0x80 [] raw_notifier_call_chain+0x11/0x20 [] dev_open+0x10a/0x120 [] dev_change_flags+0x9d/0x1e0 [] devinet_ioctl+0x6fe/0x760 [] inet_ioctl+0x94/0xc0 [] sock_ioctl+0x6a/0x290 [] vfs_ioctl+0x31/0xa0 [] do_vfs_ioctl+0x8a/0x5c0 [] sys_ioctl+0x99/0xa0 [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff -> #1 (&rdev->mtx){+.+.+.}: [] __lock_acquire+0xd76/0x12b0 [] lock_acquire+0xe3/0x120 [] mutex_lock_nested+0x44/0x350 [] cfg80211_get_dev_from_ifindex+0x60/0x90 [cfg80211] [] get_rdev_dev_by_info_ifindex+0x6f/0xa0 [cfg80211] [] nl80211_set_interface+0x3b/0x260 [cfg80211] [] genl_rcv_msg+0x1b6/0x1f0 [] netlink_rcv_skb+0x89/0xb0 [] genl_rcv+0x29/0x40 [] netlink_unicast+0x29d/0x2b0 [] netlink_sendmsg+0x214/0x300 [] sock_sendmsg+0x107/0x130 [] sys_sendmsg+0x189/0x320 [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff other info that might help us debug this: 3 locks held by phy1/1709: #0: ((wiphy_name(local->hw.wiphy))){+.+.+.}, at: [] worker_thread+0x19d/0x340 #1: (&ifmgd->work){+.+.+.}, at: [] worker_thread+0x19d/0x340 #2: (&ifmgd->mtx){+.+.+.}, at: [] ieee80211_sta_work+0x108/0x10f0 [mac80211] Reported-by: Reinette Chatre Acked-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +--- net/wireless/reg.c | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 1493285..cd7dff9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -34,9 +34,7 @@ MODULE_DESCRIPTION("wireless configuration support"); LIST_HEAD(cfg80211_rdev_list); /* - * This is used to protect the cfg80211_rdev_list, cfg80211_regdomain, - * country_ie_regdomain, the reg_beacon_list and the the last regulatory - * request receipt (last_request). + * This is used to protect the cfg80211_rdev_list */ DEFINE_MUTEX(cfg80211_mutex); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index f386981..6ab56f0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -62,6 +62,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain; */ static const struct ieee80211_regdomain *country_ie_regdomain; +/* + * Protects static reg.c components: + * - cfg80211_world_regdom + * - cfg80211_regdom + * - country_ie_regdomain + * - last_request + */ +DEFINE_MUTEX(reg_mutex); +#define assert_reg_lock() WARN_ON(!mutex_is_locked(®_mutex)) + /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -1293,7 +1303,7 @@ static void handle_channel_custom(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - assert_cfg80211_lock(); + assert_reg_lock(); sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); @@ -1342,14 +1352,14 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; - mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; handle_band_custom(wiphy, band, regd); bands_set++; } - mutex_unlock(&cfg80211_mutex); + mutex_unlock(®_mutex); /* * no point in calling this if it won't have any effect @@ -1495,7 +1505,7 @@ static int ignore_request(struct wiphy *wiphy, * Returns zero if all went fine, %-EALREADY if a regulatory domain had * already been set or other standard error codes. * - * Caller must hold &cfg80211_mutex + * Caller must hold &cfg80211_mutex and ®_mutex */ static int __regulatory_hint(struct wiphy *wiphy, struct regulatory_request *pending_request) @@ -1570,6 +1580,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) BUG_ON(!reg_request->alpha2); mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); @@ -1585,6 +1596,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (r == -EALREADY && wiphy && wiphy->strict_regulatory) wiphy_update_regulatory(wiphy, reg_request->initiator); out: + mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } @@ -1613,6 +1625,10 @@ static void reg_process_pending_beacon_hints(void) struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; + /* + * No need to hold the reg_mutex here as we just touch wiphys + * and do not read or access regulatory variables. + */ mutex_lock(&cfg80211_mutex); /* This goes through the _pending_ beacon list */ @@ -1734,12 +1750,13 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) } EXPORT_SYMBOL(regulatory_hint); +/* Caller must hold reg_mutex */ static bool reg_same_country_ie_hint(struct wiphy *wiphy, u32 country_ie_checksum) { struct wiphy *request_wiphy; - assert_cfg80211_lock(); + assert_reg_lock(); if (unlikely(last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) @@ -1776,7 +1793,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum environment_cap env = ENVIRON_ANY; struct regulatory_request *request; - mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); if (unlikely(!last_request)) goto out; @@ -1850,7 +1867,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, request->country_ie_checksum = checksum; request->country_ie_env = env; - mutex_unlock(&cfg80211_mutex); + mutex_unlock(®_mutex); queue_regulatory_request(request); @@ -1859,7 +1876,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, free_rd_out: kfree(rd); out: - mutex_unlock(&cfg80211_mutex); + mutex_unlock(®_mutex); } EXPORT_SYMBOL(regulatory_hint_11d); @@ -2192,10 +2209,13 @@ int set_regdom(const struct ieee80211_regdomain *rd) assert_cfg80211_lock(); + mutex_lock(®_mutex); + /* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); if (r) { kfree(rd); + mutex_unlock(®_mutex); return r; } @@ -2210,6 +2230,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) nl80211_send_reg_change_event(last_request); + mutex_unlock(®_mutex); + return r; } @@ -2220,16 +2242,20 @@ void reg_device_remove(struct wiphy *wiphy) assert_cfg80211_lock(); + mutex_lock(®_mutex); + kfree(wiphy->regd); if (last_request) request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) - return; + goto out; last_request->wiphy_idx = WIPHY_IDX_STALE; last_request->country_ie_env = ENVIRON_ANY; +out: + mutex_unlock(®_mutex); } int regulatory_init(void) @@ -2290,6 +2316,7 @@ void regulatory_exit(void) cancel_work_sync(®_work); mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); reset_regdomains(); @@ -2328,5 +2355,6 @@ void regulatory_exit(void) } spin_unlock(®_requests_lock); + mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } -- cgit v1.1 From 8b19e6ca3bac7e04e93fb73f561d670e77c5fae6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 30 Jul 2009 17:38:09 -0700 Subject: cfg80211: enable country IE support to all cfg80211 drivers Since the bss is always set now once we are connected, if the bss has its own information element we refer to it and pass that instead of relying on mac80211's parsing. Now all cfg80211 drivers get country IE support, automatically and we reduce the call overhead that we had on mac80211 which called this upon every beacon and instead now call this only upon a successfull connection by a STA on cfg80211. Acked-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 6 +----- net/wireless/reg.c | 6 +----- net/wireless/reg.h | 15 +++++++++++++++ net/wireless/sme.c | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2d5edfd..c9e4091 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1845,12 +1845,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, bssid, ap_ht_cap_flags); } + /* Note: country IE parsing is done for us by cfg80211 */ if (elems.country_elem) { - /* Note we are only reviewing this on beacons - * for the BSSID we are associated to */ - regulatory_hint_11d(local->hw.wiphy, - elems.country_elem, elems.country_elem_len); - /* TODO: IBSS also needs this */ if (elems.pwr_constr_elem) ieee80211_handle_pwr_constr(sdata, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6ab56f0..b3ac0aa 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1822,10 +1822,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, env = ENVIRON_OUTDOOR; /* - * 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 - * + * We will run this only upon a successful connection on cfg80211. * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ @@ -1878,7 +1875,6 @@ free_rd_out: out: mutex_unlock(®_mutex); } -EXPORT_SYMBOL(regulatory_hint_11d); static bool freq_is_chan_12_13_14(u16 freq) { diff --git a/net/wireless/reg.h b/net/wireless/reg.h index e37829a..662a9da 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -36,4 +36,19 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp); +/** + * regulatory_hint_11d - hints a country IE as a regulatory domain + * @wiphy: the wireless device giving the hint (used only for reporting + * conflicts) + * @country_ie: pointer to the country IE + * @country_ie_len: length of the country IE + * + * We will intersect the rd with the what CRDA tells us should apply + * for the alpha2 this country IE belongs to, this prevents APs from + * sending us incorrect or outdated information against a country. + */ +void regulatory_hint_11d(struct wiphy *wiphy, + u8 *country_ie, + u8 country_ie_len); + #endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3728d2b..af91192 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -13,6 +13,7 @@ #include #include #include "nl80211.h" +#include "reg.h" struct cfg80211_conn { struct cfg80211_connect_params params; @@ -320,6 +321,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 *country_ie; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif @@ -401,6 +403,20 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); + + country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + + if (!country_ie) + return; + + /* + * ieee80211_bss_get_ie() ensures we can access: + * - country_ie + 2, the start of the country ie data, and + * - and country_ie[1] which is the IE length + */ + regulatory_hint_11d(wdev->wiphy, + country_ie + 2, + country_ie[1]); } void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, -- cgit v1.1 From 75e6c3b72b3ab01c47629f3fbd0fed4e6550bf3a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 31 Jul 2009 11:18:13 +0200 Subject: cfg80211: lower dynamic PS timeout to 100ms The default of 500ms is pretty high, and leads to the device being awake at least 50% of the time under such light traffic conditions as a simple 1 second interval ping. Reduce to just 100ms -- it should have a similar effect while providing a better sleep time. Signed-off-by: Johannes Berg Reviewed-by: Kalle Valo Signed-off-by: John W. Linville --- net/wireless/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index cd7dff9..1e18930 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -671,7 +671,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE; - wdev->wext.ps_timeout = 500; + wdev->wext.ps_timeout = 100; if (rdev->ops->set_power_mgmt) if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->wext.ps, -- cgit v1.1 From a43abf293965230c93a4b74e5d10b9d60b153ab4 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 31 Jul 2009 18:54:12 +0300 Subject: mac80211: Retry probe request few times Retry 5 times (chosen arbitary ), before assuming that station is out of range. Fixes frequent disassociations while connected to weak, and sometimes even strong access points. Signed-off-by: Maxim Levitky Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 42 ++++++++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 316825b..8d790e4 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -280,6 +280,7 @@ struct ieee80211_if_managed { struct work_struct beacon_loss_work; unsigned long probe_timeout; + int probe_send_count; struct mutex mtx; struct ieee80211_bss *associated; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c9e4091..ccd5c7a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -31,6 +31,7 @@ #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 +#define IEEE80211_MAX_PROBE_TRIES 5 /* * beacon loss detection timeout @@ -1153,11 +1154,24 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } +static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ssid; + + ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID); + ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, + ssid + 2, ssid[1], NULL, 0); + + ifmgd->probe_send_count++; + ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT; + run_again(ifmgd, ifmgd->probe_timeout); +} + static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, bool beacon) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - const u8 *ssid; bool already = false; if (!netif_running(sdata->dev)) @@ -1200,18 +1214,12 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, if (already) goto out; - ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT; - mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); mutex_unlock(&sdata->local->iflist_mtx); - ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID); - ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, - ssid + 2, ssid[1], NULL, 0); - - run_again(ifmgd, ifmgd->probe_timeout); - + ifmgd->probe_send_count = 0; + ieee80211_mgd_probe_ap_send(sdata); out: mutex_unlock(&ifmgd->mtx); } @@ -2064,17 +2072,27 @@ static void ieee80211_sta_work(struct work_struct *work) if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL) && ifmgd->associated) { + u8 bssid[ETH_ALEN]; + + memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN); if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); - else { - u8 bssid[ETH_ALEN]; + + else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "No probe response from AP %pM" + " after %dms, try %d\n", bssid, + (1000 * IEEE80211_PROBE_WAIT)/HZ, + ifmgd->probe_send_count); +#endif + ieee80211_mgd_probe_ap_send(sdata); + } else { /* * We actually lost the connection ... or did we? * Let's make sure! */ ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | IEEE80211_STA_BEACON_POLL); - memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN); printk(KERN_DEBUG "No probe response from AP %pM" " after %dms, disconnecting.\n", bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); -- cgit v1.1 From d1c5091f23fed5195271e2849f89017d3a126521 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 31 Jul 2009 18:54:23 +0300 Subject: mac80211: Increase timeouts for station polling Do a probe request every 30 seconds, and wait for probe response, half a second This should lower the traffic that card sends, thus save power Wainting longer for response makes probe more robust against 'slow' access points Signed-off-by: Maxim Levitsky Acked-by: Johannes Berg Tested-by: Marcel Holtmann Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ccd5c7a..6d5a1ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -42,13 +42,13 @@ * Time the connection can be idle before we probe * it to see if we can still talk to the AP. */ -#define IEEE80211_CONNECTION_IDLE_TIME (2 * HZ) +#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ) /* * Time we wait for a probe response after sending * a probe request because of beacon loss or for * checking the connection still works. */ -#define IEEE80211_PROBE_WAIT (HZ / 5) +#define IEEE80211_PROBE_WAIT (HZ / 2) #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 -- cgit v1.1 From 3ad201496badddd8e1cda87ee6d29e8b3b8e1279 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 2 Aug 2009 02:36:49 +0300 Subject: rfkill: add the GPS radio type Althoug GPS is a technology w/o transmitting radio and thus not a primary candidate for rfkill switch, rfkill gives unified interface point for devices with wireless technology. The input key is not supplied as it is too be deprecated. Cc: johannes@sipsolutions.net Signed-off-by: Tomas Winkler Acked-by: Marcel Holtmann Signed-off-by: John W. Linville --- net/rfkill/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 044de1c..dbeaf29 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -589,11 +589,13 @@ static const char *rfkill_get_type_str(enum rfkill_type type) return "wimax"; case RFKILL_TYPE_WWAN: return "wwan"; + case RFKILL_TYPE_GPS: + return "gps"; default: BUG(); } - BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_WWAN + 1); + BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_GPS + 1); } static ssize_t rfkill_type_show(struct device *dev, -- cgit v1.1 From 8dadadb7e977a91b46ed3549f9d2f22a629e5043 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Aug 2009 09:32:23 +0200 Subject: cfg80211: clear SSID on disconnect/no connection The SME state machine in cfg80211 uses the SSID stored in struct wireless_dev internally, but fails to clear it in multiple places (when giving up on a connection attempt and when disconnecting). This doesn't matter to the SME state machine, but does matter for IBSS. Thus, in those cases, clear the SSID to avoid messing up the IBSS state machine. Reported-by: Joerg Albert Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index af91192..8a7dcbf 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -385,6 +385,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->conn = NULL; kfree(wdev->connect_keys); wdev->connect_keys = NULL; + wdev->ssid_len = 0; return; } @@ -566,6 +567,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; + wdev->ssid_len = 0; if (wdev->conn) { kfree(wdev->conn->ie); @@ -721,6 +723,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; wdev->connect_keys = NULL; + wdev->ssid_len = 0; } return err; @@ -785,6 +788,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_IDLE; kfree(wdev->conn); wdev->conn = NULL; + wdev->ssid_len = 0; return 0; } -- cgit v1.1 From e3b90ca28412fb9dcc8c5ca38e179e78fec07eee Mon Sep 17 00:00:00 2001 From: Igor Perminov Date: Tue, 4 Aug 2009 16:48:51 +0400 Subject: mac80211: FIF_PSPOLL filter flag When an interface is configured in the AP mode, the mac80211 implementation doesn't inform the driver to receive PS Poll frames. It leads to inability to communicate with power-saving stations reliably. The FIF_CONTROL flag isn't passed by mac80211 to ieee80211_ops.configure_filter when an interface is in the AP mode. And it's ok, because we don't want to receive ACK frames and other control ones, but only PS Poll ones. This patch introduces the FIF_PSPOLL filter flag in addition to FIF_CONTROL, which means for the driver "pass PS Poll frames". This flag is passed to the driver: A) When an interface is configured in the AP mode. B) In all cases, when the FIF_CONTROL flag was passed earlier (in addition to it). Signed-off-by: Igor Perminov Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/iface.c | 18 ++++++++++++++++-- net/mac80211/main.c | 3 +++ 3 files changed, 20 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8d790e4..630a438 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -628,7 +628,7 @@ struct ieee80211_local { int open_count; int monitors, cooked_mntrs; /* number of interfaces with corresponding FIF_ flags */ - int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss; + int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll; unsigned int filter_flags; /* FIF_* */ struct iw_statistics wstats; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8c1284d..e8fb03b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -220,8 +220,10 @@ static int ieee80211_open(struct net_device *dev) local->fif_fcsfail++; if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL) local->fif_plcpfail++; - if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) + if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) { local->fif_control++; + local->fif_pspoll++; + } if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; @@ -244,7 +246,14 @@ static int ieee80211_open(struct net_device *dev) spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); + } else if (sdata->vif.type == NL80211_IFTYPE_AP) { + local->fif_pspoll++; + + spin_lock_bh(&local->filter_lock); + ieee80211_configure_filter(local); + spin_unlock_bh(&local->filter_lock); } + changed |= ieee80211_reset_erp_info(sdata); ieee80211_bss_info_change_notify(sdata, changed); ieee80211_enable_keys(sdata); @@ -388,6 +397,9 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_dec(&local->iff_promiscs); + if (sdata->vif.type == NL80211_IFTYPE_AP) + local->fif_pspoll--; + netif_addr_lock_bh(dev); spin_lock_bh(&local->filter_lock); __dev_addr_unsync(&local->mc_list, &local->mc_count, @@ -439,8 +451,10 @@ static int ieee80211_stop(struct net_device *dev) local->fif_fcsfail--; if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL) local->fif_plcpfail--; - if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) + if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) { + local->fif_pspoll--; local->fif_control--; + } if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 22e0738..0c4f8e1 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -77,6 +77,9 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (local->fif_other_bss) new_flags |= FIF_OTHER_BSS; + if (local->fif_pspoll) + new_flags |= FIF_PSPOLL; + changed_flags = local->filter_flags ^ new_flags; /* be a bit nasty */ -- cgit v1.1 From f816700aa9ef1b1e2f984f638cb211e79dcab495 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 4 Aug 2009 20:18:33 -0700 Subject: xfrm4: fix build when SYSCTLs are disabled Fix build errors when SYSCTLs are not enabled: (.init.text+0x5154): undefined reference to `net_ipv4_ctl_path' (.init.text+0x5176): undefined reference to `register_net_sysctl_table' xfrm4_policy.c:(.exit.text+0x573): undefined reference to `unregister_net_sysctl_table Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- net/ipv4/xfrm4_policy.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 1ba4474..74fb2eb 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -264,6 +264,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { .fill_dst = xfrm4_fill_dst, }; +#ifdef CONFIG_SYSCTL static struct ctl_table xfrm4_policy_table[] = { { .ctl_name = CTL_UNNUMBERED, @@ -277,6 +278,7 @@ static struct ctl_table xfrm4_policy_table[] = { }; static struct ctl_table_header *sysctl_hdr; +#endif static void __init xfrm4_policy_init(void) { @@ -285,8 +287,10 @@ static void __init xfrm4_policy_init(void) static void __exit xfrm4_policy_fini(void) { +#ifdef CONFIG_SYSCTL if (sysctl_hdr) unregister_net_sysctl_table(sysctl_hdr); +#endif xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); } @@ -305,7 +309,9 @@ void __init xfrm4_init(int rt_max_size) * and start cleaning when were 1/2 full */ xfrm4_dst_ops.gc_thresh = rt_max_size/2; +#ifdef CONFIG_SYSCTL sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv4_ctl_path, xfrm4_policy_table); +#endif } -- cgit v1.1 From db71789c01ae7b641f83c5aa64e7df25122f4b28 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 4 Aug 2009 20:32:16 -0700 Subject: xfrm6: Fix xfrm6_policy.c build when SYSCTL disabled. Same as how Randy Dunlap fixed the ipv4 side of things. Signed-off-by: David S. Miller --- net/ipv6/xfrm6_policy.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 611cffc..8ec3d45 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -306,6 +306,7 @@ static void xfrm6_policy_fini(void) xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo); } +#ifdef CONFIG_SYSCTL static struct ctl_table xfrm6_policy_table[] = { { .ctl_name = CTL_UNNUMBERED, @@ -319,6 +320,7 @@ static struct ctl_table xfrm6_policy_table[] = { }; static struct ctl_table_header *sysctl_hdr; +#endif int __init xfrm6_init(void) { @@ -345,9 +347,10 @@ int __init xfrm6_init(void) */ gc_thresh = FIB6_TABLE_HASHSZ * 8; xfrm6_dst_ops.gc_thresh = (gc_thresh < 1024) ? 1024 : gc_thresh; - +#ifdef CONFIG_SYSCTL sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv6_ctl_path, xfrm6_policy_table); +#endif out: return ret; out_policy: @@ -357,8 +360,10 @@ out_policy: void xfrm6_fini(void) { +#ifdef CONFIG_SYSCTL if (sysctl_hdr) unregister_net_sysctl_table(sysctl_hdr); +#endif //xfrm6_input_fini(); xfrm6_policy_fini(); xfrm6_state_fini(); -- cgit v1.1 From 36cbd3dcc10384f813ec0814255f576c84f2bcd4 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 5 Aug 2009 10:42:58 -0700 Subject: net: mark read-only arrays as const String literals are constant, and usually, we can also tag the array of pointers const too, moving it to the .rodata section. Signed-off-by: Jan Engelhardt Signed-off-by: David S. Miller --- net/8021q/vlanproc.c | 2 +- net/atm/lec.c | 9 +++++---- net/atm/proc.c | 9 +++++---- net/bluetooth/af_bluetooth.c | 4 ++-- net/bridge/br_stp.c | 2 +- net/core/dev.c | 2 +- net/core/net-sysfs.c | 2 +- net/core/sock.c | 6 +++--- net/dccp/ccids/ccid3.c | 4 ++-- net/dccp/feat.c | 7 ++++--- net/dccp/proto.c | 4 ++-- net/ipv4/fib_trie.c | 2 +- net/ipv6/proc.c | 4 ++-- net/irda/ircomm/ircomm_event.c | 4 ++-- net/irda/ircomm/ircomm_tty_attach.c | 4 ++-- net/irda/iriap.c | 4 ++-- net/irda/irlan/irlan_common.c | 4 ++-- net/irda/irlap.c | 2 +- net/irda/irlap_event.c | 4 ++-- net/irda/irlmp_event.c | 6 +++--- net/llc/llc_proc.c | 2 +- net/netfilter/ipvs/ip_vs_proto.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 2 +- net/rds/ib_stats.c | 2 +- net/rds/iw_stats.c | 2 +- net/rds/rds.h | 3 ++- net/rds/stats.c | 4 ++-- net/rxrpc/ar-ack.c | 2 +- net/sctp/debug.c | 14 +++++++------- 30 files changed, 63 insertions(+), 58 deletions(-) (limited to 'net') diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index b55a091..6262c33 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -107,7 +107,7 @@ static const struct file_operations vlandev_fops = { */ /* Strings */ -static const char *vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = { +static const char *const vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = { [VLAN_NAME_TYPE_RAW_PLUS_VID] = "VLAN_NAME_TYPE_RAW_PLUS_VID", [VLAN_NAME_TYPE_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD", [VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD", diff --git a/net/atm/lec.c b/net/atm/lec.c index c463868..8e723c2 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -935,9 +935,9 @@ static int lecd_attach(struct atm_vcc *vcc, int arg) } #ifdef CONFIG_PROC_FS -static char *lec_arp_get_status_string(unsigned char status) +static const char *lec_arp_get_status_string(unsigned char status) { - static char *lec_arp_status_string[] = { + static const char *const lec_arp_status_string[] = { "ESI_UNKNOWN ", "ESI_ARP_PENDING ", "ESI_VC_PENDING ", @@ -1121,7 +1121,8 @@ static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos) static int lec_seq_show(struct seq_file *seq, void *v) { - static char lec_banner[] = "Itf MAC ATM destination" + static const char lec_banner[] = + "Itf MAC ATM destination" " Status Flags " "VPI/VCI Recv VPI/VCI\n"; @@ -1505,7 +1506,7 @@ lec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove) } #if DEBUG_ARP_TABLE -static char *get_status_string(unsigned char st) +static const char *get_status_string(unsigned char st) { switch (st) { case ESI_UNKNOWN: diff --git a/net/atm/proc.c b/net/atm/proc.c index 38de5ff..ab8419a 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -151,8 +151,9 @@ static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) { - static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" }; - static const char *aal_name[] = { + static const char *const class_name[] = + {"off","UBR","CBR","VBR","ABR"}; + static const char *const aal_name[] = { "---", "1", "2", "3/4", /* 0- 3 */ "???", "5", "???", "???", /* 4- 7 */ "???", "???", "???", "???", /* 8-11 */ @@ -178,7 +179,7 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) static const char *vcc_state(struct atm_vcc *vcc) { - static const char *map[] = { ATM_VS2TXT_MAP }; + static const char *const map[] = { ATM_VS2TXT_MAP }; return map[ATM_VF2VS(vcc->flags)]; } @@ -335,7 +336,7 @@ static const struct file_operations vcc_seq_fops = { static int svc_seq_show(struct seq_file *seq, void *v) { - static char atm_svc_banner[] = + static const char atm_svc_banner[] = "Itf VPI VCI State Remote\n"; if (v == SEQ_START_TOKEN) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 0250e06..8cfb5a8 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -49,7 +49,7 @@ static struct net_proto_family *bt_proto[BT_MAX_PROTO]; static DEFINE_RWLOCK(bt_proto_lock); static struct lock_class_key bt_lock_key[BT_MAX_PROTO]; -static const char *bt_key_strings[BT_MAX_PROTO] = { +static const char *const bt_key_strings[BT_MAX_PROTO] = { "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP", "sk_lock-AF_BLUETOOTH-BTPROTO_HCI", "sk_lock-AF_BLUETOOTH-BTPROTO_SCO", @@ -61,7 +61,7 @@ static const char *bt_key_strings[BT_MAX_PROTO] = { }; static struct lock_class_key bt_slock_key[BT_MAX_PROTO]; -static const char *bt_slock_key_strings[BT_MAX_PROTO] = { +static const char *const bt_slock_key_strings[BT_MAX_PROTO] = { "slock-AF_BLUETOOTH-BTPROTO_L2CAP", "slock-AF_BLUETOOTH-BTPROTO_HCI", "slock-AF_BLUETOOTH-BTPROTO_SCO", diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 0660515..fd3f8d6 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -21,7 +21,7 @@ */ #define MESSAGE_AGE_INCR ((HZ < 256) ? 1 : (HZ/256)) -static const char *br_port_state_names[] = { +static const char *const br_port_state_names[] = { [BR_STATE_DISABLED] = "disabled", [BR_STATE_LISTENING] = "listening", [BR_STATE_LEARNING] = "learning", diff --git a/net/core/dev.c b/net/core/dev.c index 7134766..f01a9c4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -272,7 +272,7 @@ static const unsigned short netdev_lock_type[] = ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_IEEE802154_PHY, ARPHRD_VOID, ARPHRD_NONE}; -static const char *netdev_lock_name[] = +static const char *const netdev_lock_name[] = {"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25", "_xmit_PRONET", "_xmit_CHAOS", "_xmit_IEEE802", "_xmit_ARCNET", "_xmit_APPLETLK", "_xmit_DLCI", "_xmit_ATM", "_xmit_METRICOM", diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 3994680..ad91e9e 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -141,7 +141,7 @@ static ssize_t show_dormant(struct device *dev, return -EINVAL; } -static const char *operstates[] = { +static const char *const operstates[] = { "unknown", "notpresent", /* currently unused */ "down", diff --git a/net/core/sock.c b/net/core/sock.c index bbb25be..a324a80 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -142,7 +142,7 @@ static struct lock_class_key af_family_slock_keys[AF_MAX]; * strings build-time, so that runtime initialization of socket * locks is fast): */ -static const char *af_family_key_strings[AF_MAX+1] = { +static const char *const af_family_key_strings[AF_MAX+1] = { "sk_lock-AF_UNSPEC", "sk_lock-AF_UNIX" , "sk_lock-AF_INET" , "sk_lock-AF_AX25" , "sk_lock-AF_IPX" , "sk_lock-AF_APPLETALK", "sk_lock-AF_NETROM", "sk_lock-AF_BRIDGE" , "sk_lock-AF_ATMPVC" , @@ -158,7 +158,7 @@ static const char *af_family_key_strings[AF_MAX+1] = { "sk_lock-AF_IEEE802154", "sk_lock-AF_MAX" }; -static const char *af_family_slock_key_strings[AF_MAX+1] = { +static const char *const af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" , "slock-AF_AX25" , "slock-AF_IPX" , "slock-AF_APPLETALK", "slock-AF_NETROM", "slock-AF_BRIDGE" , "slock-AF_ATMPVC" , @@ -174,7 +174,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = { "slock-AF_IEEE802154", "slock-AF_MAX" }; -static const char *af_family_clock_key_strings[AF_MAX+1] = { +static const char *const af_family_clock_key_strings[AF_MAX+1] = { "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" , "clock-AF_AX25" , "clock-AF_IPX" , "clock-AF_APPLETALK", "clock-AF_NETROM", "clock-AF_BRIDGE" , "clock-AF_ATMPVC" , diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index a27b7f4..f596ce1 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -52,7 +52,7 @@ static int ccid3_debug; #ifdef CONFIG_IP_DCCP_CCID3_DEBUG static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state) { - static char *ccid3_state_names[] = { + static const char *const ccid3_state_names[] = { [TFRC_SSTATE_NO_SENT] = "NO_SENT", [TFRC_SSTATE_NO_FBACK] = "NO_FBACK", [TFRC_SSTATE_FBACK] = "FBACK", @@ -646,7 +646,7 @@ enum ccid3_fback_type { #ifdef CONFIG_IP_DCCP_CCID3_DEBUG static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state) { - static char *ccid3_rx_state_names[] = { + static const char *const ccid3_rx_state_names[] = { [TFRC_RSTATE_NO_DATA] = "NO_DATA", [TFRC_RSTATE_DATA] = "DATA", [TFRC_RSTATE_TERM] = "TERM", diff --git a/net/dccp/feat.c b/net/dccp/feat.c index b04160a..972b8dc 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -213,7 +213,7 @@ static int dccp_feat_default_value(u8 feat_num) */ static const char *dccp_feat_fname(const u8 feat) { - static const char *feature_names[] = { + static const char *const feature_names[] = { [DCCPF_RESERVED] = "Reserved", [DCCPF_CCID] = "CCID", [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", @@ -236,8 +236,9 @@ static const char *dccp_feat_fname(const u8 feat) return feature_names[feat]; } -static const char *dccp_feat_sname[] = { "DEFAULT", "INITIALISING", "CHANGING", - "UNSTABLE", "STABLE" }; +static const char *const dccp_feat_sname[] = { + "DEFAULT", "INITIALISING", "CHANGING", "UNSTABLE", "STABLE", +}; #ifdef CONFIG_IP_DCCP_DEBUG static const char *dccp_feat_oname(const u8 opt) diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 94ca8ea..37b3b42 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -124,7 +124,7 @@ EXPORT_SYMBOL_GPL(dccp_done); const char *dccp_packet_name(const int type) { - static const char *dccp_packet_names[] = { + static const char *const dccp_packet_names[] = { [DCCP_PKT_REQUEST] = "REQUEST", [DCCP_PKT_RESPONSE] = "RESPONSE", [DCCP_PKT_DATA] = "DATA", @@ -147,7 +147,7 @@ EXPORT_SYMBOL_GPL(dccp_packet_name); const char *dccp_state_name(const int state) { - static char *dccp_state_names[] = { + static const char *const dccp_state_names[] = { [DCCP_OPEN] = "OPEN", [DCCP_REQUESTING] = "REQUESTING", [DCCP_PARTOPEN] = "PARTOPEN", diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index d58b491..fe3c846 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2421,7 +2421,7 @@ static inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s) } } -static const char *rtn_type_names[__RTN_MAX] = { +static const char *const rtn_type_names[__RTN_MAX] = { [RTN_UNSPEC] = "UNSPEC", [RTN_UNICAST] = "UNICAST", [RTN_LOCAL] = "LOCAL", diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 590ddef..c9605c3 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -101,7 +101,7 @@ static struct snmp_mib snmp6_icmp6_list[] = { }; /* RFC 4293 v6 ICMPMsgStatsTable; named items for RFC 2466 compatibility */ -static char *icmp6type2name[256] = { +static const char *const icmp6type2name[256] = { [ICMPV6_DEST_UNREACH] = "DestUnreachs", [ICMPV6_PKT_TOOBIG] = "PktTooBigs", [ICMPV6_TIME_EXCEED] = "TimeExcds", @@ -144,7 +144,7 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void **mib) /* print by name -- deprecated items */ for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { int icmptype; - char *p; + const char *p; icmptype = i & 0xff; p = icmp6type2name[icmptype]; diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c index c35b3ef..d78554f 100644 --- a/net/irda/ircomm/ircomm_event.c +++ b/net/irda/ircomm/ircomm_event.c @@ -49,7 +49,7 @@ static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb, struct ircomm_info *info); -char *ircomm_state[] = { +const char *const ircomm_state[] = { "IRCOMM_IDLE", "IRCOMM_WAITI", "IRCOMM_WAITR", @@ -57,7 +57,7 @@ char *ircomm_state[] = { }; #ifdef CONFIG_IRDA_DEBUG -static char *ircomm_event[] = { +static const char *const ircomm_event[] = { "IRCOMM_CONNECT_REQUEST", "IRCOMM_CONNECT_RESPONSE", "IRCOMM_TTP_CONNECT_INDICATION", diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index 9032a1d..eafc010 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -80,7 +80,7 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, struct sk_buff *skb, struct ircomm_tty_info *info); -char *ircomm_tty_state[] = { +const char *const ircomm_tty_state[] = { "IRCOMM_TTY_IDLE", "IRCOMM_TTY_SEARCH", "IRCOMM_TTY_QUERY_PARAMETERS", @@ -91,7 +91,7 @@ char *ircomm_tty_state[] = { }; #ifdef CONFIG_IRDA_DEBUG -static char *ircomm_tty_event[] = { +static const char *const ircomm_tty_event[] = { "IRCOMM_TTY_ATTACH_CABLE", "IRCOMM_TTY_DETACH_CABLE", "IRCOMM_TTY_DATA_REQUEST", diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 4a105dc..294e34d 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -44,7 +44,7 @@ #ifdef CONFIG_IRDA_DEBUG /* FIXME: This one should go in irlmp.c */ -static const char *ias_charset_types[] = { +static const char *const ias_charset_types[] = { "CS_ASCII", "CS_ISO_8859_1", "CS_ISO_8859_2", @@ -966,7 +966,7 @@ static void iriap_watchdog_timer_expired(void *data) #ifdef CONFIG_PROC_FS -static const char *ias_value_types[] = { +static const char *const ias_value_types[] = { "IAS_MISSING", "IAS_INTEGER", "IAS_OCT_SEQ", diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 774d73a..6211682 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -69,14 +69,14 @@ static int eth; /* Use "eth" or "irlan" name for devices */ static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ #ifdef CONFIG_PROC_FS -static const char *irlan_access[] = { +static const char *const irlan_access[] = { "UNKNOWN", "DIRECT", "PEER", "HOSTED" }; -static const char *irlan_media[] = { +static const char *const irlan_media[] = { "UNKNOWN", "802.3", "802.5" diff --git a/net/irda/irlap.c b/net/irda/irlap.c index e4965b7..356e65b 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -63,7 +63,7 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self, struct qos_info *qos_user); #ifdef CONFIG_IRDA_DEBUG -static char *lap_reasons[] = { +static const char *const lap_reasons[] = { "ERROR, NOT USED", "LAP_DISC_INDICATION", "LAP_NO_RESPONSE", diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index 16c4ef0..c5c5195 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -78,7 +78,7 @@ static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, struct sk_buff *, struct irlap_info *); #ifdef CONFIG_IRDA_DEBUG -static const char *irlap_event[] = { +static const char *const irlap_event[] = { "DISCOVERY_REQUEST", "CONNECT_REQUEST", "CONNECT_RESPONSE", @@ -120,7 +120,7 @@ static const char *irlap_event[] = { }; #endif /* CONFIG_IRDA_DEBUG */ -const char *irlap_state[] = { +const char *const irlap_state[] = { "LAP_NDM", "LAP_QUERY", "LAP_REPLY", diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c index 78cce0c..c1fb5db 100644 --- a/net/irda/irlmp_event.c +++ b/net/irda/irlmp_event.c @@ -33,13 +33,13 @@ #include #include -const char *irlmp_state[] = { +const char *const irlmp_state[] = { "LAP_STANDBY", "LAP_U_CONNECT", "LAP_ACTIVE", }; -const char *irlsap_state[] = { +const char *const irlsap_state[] = { "LSAP_DISCONNECTED", "LSAP_CONNECT", "LSAP_CONNECT_PEND", @@ -49,7 +49,7 @@ const char *irlsap_state[] = { }; #ifdef CONFIG_IRDA_DEBUG -static const char *irlmp_event[] = { +static const char *const irlmp_event[] = { "LM_CONNECT_REQUEST", "LM_CONNECT_CONFIRM", "LM_CONNECT_RESPONSE", diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index f97be47..be47ac4 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -143,7 +143,7 @@ out: return 0; } -static char *llc_conn_state_names[] = { +static const char *const llc_conn_state_names[] = { [LLC_CONN_STATE_ADM] = "adm", [LLC_CONN_STATE_SETUP] = "setup", [LLC_CONN_STATE_NORMAL] = "normal", diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 85c8892..3e76716 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -127,7 +127,8 @@ ip_vs_create_timeout_table(int *table, int size) * Set timeout value for state specified by name */ int -ip_vs_set_state_timeout(int *table, int num, char **names, char *name, int to) +ip_vs_set_state_timeout(int *table, int num, const char *const *names, + const char *name, int to) { int i; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 2278e14..91d28e0 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -377,7 +377,7 @@ static int tcp_timeouts[IP_VS_TCP_S_LAST+1] = { [IP_VS_TCP_S_LAST] = 2*HZ, }; -static char * tcp_state_name_table[IP_VS_TCP_S_LAST+1] = { +static const char *const tcp_state_name_table[IP_VS_TCP_S_LAST+1] = { [IP_VS_TCP_S_NONE] = "NONE", [IP_VS_TCP_S_ESTABLISHED] = "ESTABLISHED", [IP_VS_TCP_S_SYN_SENT] = "SYN_SENT", diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 33a05d3..e7a6885 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -472,7 +472,7 @@ static int udp_timeouts[IP_VS_UDP_S_LAST+1] = { [IP_VS_UDP_S_LAST] = 2*HZ, }; -static char * udp_state_name_table[IP_VS_UDP_S_LAST+1] = { +static const char *const udp_state_name_table[IP_VS_UDP_S_LAST+1] = { [IP_VS_UDP_S_NORMAL] = "UDP", [IP_VS_UDP_S_LAST] = "BUG!", }; diff --git a/net/rds/ib_stats.c b/net/rds/ib_stats.c index 02e3e3d..8d84883 100644 --- a/net/rds/ib_stats.c +++ b/net/rds/ib_stats.c @@ -39,7 +39,7 @@ DEFINE_PER_CPU(struct rds_ib_statistics, rds_ib_stats) ____cacheline_aligned; -static char *rds_ib_stat_names[] = { +static const char *const rds_ib_stat_names[] = { "ib_connect_raced", "ib_listen_closed_stale", "ib_tx_cq_call", diff --git a/net/rds/iw_stats.c b/net/rds/iw_stats.c index ccc7e8f..d33ea79 100644 --- a/net/rds/iw_stats.c +++ b/net/rds/iw_stats.c @@ -39,7 +39,7 @@ DEFINE_PER_CPU(struct rds_iw_statistics, rds_iw_stats) ____cacheline_aligned; -static char *rds_iw_stat_names[] = { +static const char *const rds_iw_stat_names[] = { "iw_connect_raced", "iw_listen_closed_stale", "iw_tx_cq_call", diff --git a/net/rds/rds.h b/net/rds/rds.h index dbe1112..290566c 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -652,7 +652,8 @@ DECLARE_PER_CPU_SHARED_ALIGNED(struct rds_statistics, rds_stats); int __init rds_stats_init(void); void rds_stats_exit(void); void rds_stats_info_copy(struct rds_info_iterator *iter, - uint64_t *values, char **names, size_t nr); + uint64_t *values, const char *const *names, + size_t nr); /* sysctl.c */ int __init rds_sysctl_init(void); diff --git a/net/rds/stats.c b/net/rds/stats.c index 6371468..91d8c58 100644 --- a/net/rds/stats.c +++ b/net/rds/stats.c @@ -40,7 +40,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_statistics, rds_stats); /* :.,$s/unsigned long\>.*\ Date: Tue, 4 Aug 2009 07:28:28 +0000 Subject: net: implement a SO_PROTOCOL getsockoption Similar to SO_TYPE returning the socket type, SO_PROTOCOL allows to retrieve the protocol used with a given socket. I am not quite sure why we have that-many copies of socket.h, and why the values are not the same on all arches either, but for where hex numbers dominate, I use 0x1029 for SO_PROTOCOL as that seems to be the next free unused number across a bunch of operating systems, or so Google results make me want to believe. SO_PROTOCOL for others just uses the next free Linux number, 38. Signed-off-by: Jan Engelhardt Signed-off-by: David S. Miller --- net/core/sock.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net') diff --git a/net/core/sock.c b/net/core/sock.c index a324a80..ebce661 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -482,6 +482,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, sk->sk_reuse = valbool; break; case SO_TYPE: + case SO_PROTOCOL: case SO_ERROR: ret = -ENOPROTOOPT; break; @@ -764,6 +765,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_type; break; + case SO_PROTOCOL: + v.val = sk->sk_protocol; + break; + case SO_ERROR: v.val = -sock_error(sk); if (v.val == 0) -- cgit v1.1 From 0d6038ee76f2e06b79d0465807f67e86bf4025de Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 4 Aug 2009 07:28:29 +0000 Subject: net: implement a SO_DOMAIN getsockoption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This sockopt goes in line with SO_TYPE and SO_PROTOCOL. It makes it possible for userspace programs to pass around file descriptors — I am referring to arguments-to-functions, but it may even work for the fd passing over UNIX sockets — without needing to also pass the auxiliary information (PF_INET6/IPPROTO_TCP). Signed-off-by: Jan Engelhardt Signed-off-by: David S. Miller --- net/core/sock.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net') diff --git a/net/core/sock.c b/net/core/sock.c index ebce661..3ac34ea 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -483,6 +483,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, break; case SO_TYPE: case SO_PROTOCOL: + case SO_DOMAIN: case SO_ERROR: ret = -ENOPROTOOPT; break; @@ -769,6 +770,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_protocol; break; + case SO_DOMAIN: + v.val = sk->sk_family; + break; + case SO_ERROR: v.val = -sock_error(sk); if (v.val == 0) -- cgit v1.1 From 3d7ddd540b4c2d24c6a3e7a52c083a0c31e6151c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 5 Aug 2009 20:30:13 -0700 Subject: net/rds: Use AF_INET for sin_family field Elsewhere the sin_family field holds a value with a name of the form AF_..., so it seems reasonable to do so here as well. Also the values of PF_INET and AF_INET are the same. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ struct sockaddr_in sip; @@ ( sip.sin_family == - PF_INET + AF_INET | sip.sin_family != - PF_INET + AF_INET | sip.sin_family = - PF_INET + AF_INET ) // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/rds/rdma_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index 981a5e6..7a6c748 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -137,7 +137,7 @@ static int __init rds_rdma_listen_init(void) goto out; } - sin.sin_family = PF_INET, + sin.sin_family = AF_INET, sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); sin.sin_port = (__force u16)htons(RDS_PORT); -- cgit v1.1 From bbd8a0d3a3b65d341437f8b99c828fa5cc29c739 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Thu, 6 Aug 2009 01:44:21 +0000 Subject: net: Avoid enqueuing skb for default qdiscs dev_queue_xmit enqueue's a skb and calls qdisc_run which dequeue's the skb and xmits it. In most cases, the skb that is enqueue'd is the same one that is dequeue'd (unless the queue gets stopped or multiple cpu's write to the same queue and ends in a race with qdisc_run). For default qdiscs, we can remove the redundant enqueue/dequeue and simply xmit the skb since the default qdisc is work-conserving. The patch uses a new flag - TCQ_F_CAN_BYPASS to identify the default fast queue. The controversial part of the patch is incrementing qlen when a skb is requeued - this is to avoid checks like the second line below: + } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) && >> !q->gso_skb && + !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) { Results of a 2 hour testing for multiple netperf sessions (1, 2, 4, 8, 12 sessions on a 4 cpu system-X). The BW numbers are aggregate Mb/s across iterations tested with this version on System-X boxes with Chelsio 10gbps cards: ---------------------------------- Size | ORG BW NEW BW | ---------------------------------- 128K | 156964 159381 | 256K | 158650 162042 | ---------------------------------- Changes from ver1: 1. Move sch_direct_xmit declaration from sch_generic.h to pkt_sched.h 2. Update qdisc basic statistics for direct xmit path. 3. Set qlen to zero in qdisc_reset. 4. Changed some function names to more meaningful ones. Signed-off-by: Krishna Kumar Signed-off-by: David S. Miller --- net/core/dev.c | 48 ++++++++++++++++++------- net/sched/sch_generic.c | 93 ++++++++++++++++++++++++++++++------------------- 2 files changed, 92 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index f01a9c4..a0bc087 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1786,6 +1786,40 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, return netdev_get_tx_queue(dev, queue_index); } +static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, + struct net_device *dev, + struct netdev_queue *txq) +{ + spinlock_t *root_lock = qdisc_lock(q); + int rc; + + spin_lock(root_lock); + if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) { + kfree_skb(skb); + rc = NET_XMIT_DROP; + } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) && + !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) { + /* + * This is a work-conserving queue; there are no old skbs + * waiting to be sent out; and the qdisc is not running - + * xmit the skb directly. + */ + __qdisc_update_bstats(q, skb->len); + if (sch_direct_xmit(skb, q, dev, txq, root_lock)) + __qdisc_run(q); + else + clear_bit(__QDISC_STATE_RUNNING, &q->state); + + rc = NET_XMIT_SUCCESS; + } else { + rc = qdisc_enqueue_root(skb, q); + qdisc_run(q); + } + spin_unlock(root_lock); + + return rc; +} + /** * dev_queue_xmit - transmit a buffer * @skb: buffer to transmit @@ -1859,19 +1893,7 @@ gso: skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); #endif if (q->enqueue) { - spinlock_t *root_lock = qdisc_lock(q); - - spin_lock(root_lock); - - if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) { - kfree_skb(skb); - rc = NET_XMIT_DROP; - } else { - rc = qdisc_enqueue_root(skb, q); - qdisc_run(q); - } - spin_unlock(root_lock); - + rc = __dev_xmit_skb(skb, q, dev, txq); goto out; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 27d0381..693df7a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -37,15 +37,11 @@ * - updates to tree and tree walking are only done under the rtnl mutex. */ -static inline int qdisc_qlen(struct Qdisc *q) -{ - return q->q.qlen; -} - static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { q->gso_skb = skb; q->qstats.requeues++; + q->q.qlen++; /* it's still part of the queue */ __netif_schedule(q); return 0; @@ -61,9 +57,11 @@ static inline struct sk_buff *dequeue_skb(struct Qdisc *q) /* check the reason of requeuing without tx lock first */ txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); - if (!netif_tx_queue_stopped(txq) && !netif_tx_queue_frozen(txq)) + if (!netif_tx_queue_stopped(txq) && + !netif_tx_queue_frozen(txq)) { q->gso_skb = NULL; - else + q->q.qlen--; + } else skb = NULL; } else { skb = q->dequeue(q); @@ -103,44 +101,23 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, } /* - * NOTE: Called under qdisc_lock(q) with locally disabled BH. - * - * __QDISC_STATE_RUNNING guarantees only one CPU can process - * this qdisc at a time. qdisc_lock(q) serializes queue accesses for - * this queue. - * - * netif_tx_lock serializes accesses to device driver. - * - * qdisc_lock(q) and netif_tx_lock are mutually exclusive, - * if one is grabbed, another must be free. - * - * Note, that this procedure can be called by a watchdog timer + * Transmit one skb, and handle the return status as required. Holding the + * __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this + * function. * * Returns to the caller: * 0 - queue is empty or throttled. * >0 - queue is not empty. - * */ -static inline int qdisc_restart(struct Qdisc *q) +int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, + struct net_device *dev, struct netdev_queue *txq, + spinlock_t *root_lock) { - struct netdev_queue *txq; int ret = NETDEV_TX_BUSY; - struct net_device *dev; - spinlock_t *root_lock; - struct sk_buff *skb; - - /* Dequeue packet */ - if (unlikely((skb = dequeue_skb(q)) == NULL)) - return 0; - - root_lock = qdisc_lock(q); /* And release qdisc */ spin_unlock(root_lock); - dev = qdisc_dev(q); - txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); - HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_tx_queue_stopped(txq) && !netif_tx_queue_frozen(txq)) @@ -177,6 +154,44 @@ static inline int qdisc_restart(struct Qdisc *q) return ret; } +/* + * NOTE: Called under qdisc_lock(q) with locally disabled BH. + * + * __QDISC_STATE_RUNNING guarantees only one CPU can process + * this qdisc at a time. qdisc_lock(q) serializes queue accesses for + * this queue. + * + * netif_tx_lock serializes accesses to device driver. + * + * qdisc_lock(q) and netif_tx_lock are mutually exclusive, + * if one is grabbed, another must be free. + * + * Note, that this procedure can be called by a watchdog timer + * + * Returns to the caller: + * 0 - queue is empty or throttled. + * >0 - queue is not empty. + * + */ +static inline int qdisc_restart(struct Qdisc *q) +{ + struct netdev_queue *txq; + struct net_device *dev; + spinlock_t *root_lock; + struct sk_buff *skb; + + /* Dequeue packet */ + skb = dequeue_skb(q); + if (unlikely(!skb)) + return 0; + + root_lock = qdisc_lock(q); + dev = qdisc_dev(q); + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + + return sch_direct_xmit(skb, q, dev, txq, root_lock); +} + void __qdisc_run(struct Qdisc *q) { unsigned long start_time = jiffies; @@ -547,8 +562,11 @@ void qdisc_reset(struct Qdisc *qdisc) if (ops->reset) ops->reset(qdisc); - kfree_skb(qdisc->gso_skb); - qdisc->gso_skb = NULL; + if (qdisc->gso_skb) { + kfree_skb(qdisc->gso_skb); + qdisc->gso_skb = NULL; + qdisc->q.qlen = 0; + } } EXPORT_SYMBOL(qdisc_reset); @@ -605,6 +623,9 @@ static void attach_one_default_qdisc(struct net_device *dev, printk(KERN_INFO "%s: activation failed\n", dev->name); return; } + + /* Can by-pass the queue discipline for default qdisc */ + qdisc->flags |= TCQ_F_CAN_BYPASS; } else { qdisc = &noqueue_qdisc; } -- cgit v1.1 From 549812799c8495451e71ebd9f6a862b33120a35a Mon Sep 17 00:00:00 2001 From: Rafael Laufer Date: Mon, 10 Aug 2009 10:08:27 +0200 Subject: netfilter: nf_conntrack: add SCTP support for SO_ORIGINAL_DST Signed-off-by: Patrick McHardy --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 7d2ead7..05a9bc8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -256,11 +256,11 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len) tuple.dst.u3.ip = inet->daddr; tuple.dst.u.tcp.port = inet->dport; tuple.src.l3num = PF_INET; - tuple.dst.protonum = IPPROTO_TCP; + tuple.dst.protonum = sk->sk_protocol; - /* We only do TCP at the moment: is there a better way? */ - if (strcmp(sk->sk_prot->name, "TCP")) { - pr_debug("SO_ORIGINAL_DST: Not a TCP socket\n"); + /* We only do TCP and SCTP at the moment: is there a better way? */ + if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) { + pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n"); return -ENOPROTOOPT; } -- cgit v1.1 From be39ee11cd1f67b51ac8e71d177a981eb34f2ab2 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 10 Aug 2009 10:10:55 +0200 Subject: netfilter: ebtables: Use %pM conversion specifier ebt_log uses its own implementation of print_mac to print MAC addresses. This patch converts it to use the %pM conversion specifier for printk. Signed-off-by: Tobias Klauser Signed-off-by: Patrick McHardy --- net/bridge/netfilter/ebt_log.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index a94f3cc..e4ea3fd 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -50,14 +50,6 @@ struct arppayload unsigned char ip_dst[4]; }; -static void print_MAC(const unsigned char *p) -{ - int i; - - for (i = 0; i < ETH_ALEN; i++, p++) - printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':'); -} - static void print_ports(const struct sk_buff *skb, uint8_t protocol, int offset) { @@ -88,14 +80,11 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum, unsigned int bitmask; spin_lock_bh(&ebt_log_lock); - printk("<%c>%s IN=%s OUT=%s MAC source = ", '0' + loginfo->u.log.level, - prefix, in ? in->name : "", out ? out->name : ""); - - print_MAC(eth_hdr(skb)->h_source); - printk("MAC dest = "); - print_MAC(eth_hdr(skb)->h_dest); - - printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto)); + printk("<%c>%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x", + '0' + loginfo->u.log.level, prefix, + in ? in->name : "", out ? out->name : "", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); if (loginfo->type == NF_LOG_TYPE_LOG) bitmask = loginfo->u.log.logflags; @@ -171,12 +160,8 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum, printk(" INCOMPLETE ARP payload"); goto out; } - printk(" ARP MAC SRC="); - print_MAC(ap->mac_src); - printk(" ARP IP SRC=%pI4", ap->ip_src); - printk(" ARP MAC DST="); - print_MAC(ap->mac_dst); - printk(" ARP IP DST=%pI4", ap->ip_dst); + printk(" ARP MAC SRC=%pM ARP IP SRC=%pI4 ARP MAC DST=%pM ARP IP DST=%pI4", + ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst); } } out: -- cgit v1.1 From 7cd1837b5d24417eca667d674a97bea936849785 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:36:33 +0200 Subject: netfilter: xtables: remove xt_TOS v0 Superseded by xt_TOS v1 (v2.6.24-2396-g5c350e5). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_DSCP.c | 46 ---------------------------------------------- net/netfilter/xt_dscp.c | 17 ----------------- 2 files changed, 63 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index 6a347e7..74ce892 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -18,7 +18,6 @@ #include #include -#include MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); @@ -73,41 +72,6 @@ static bool dscp_tg_check(const struct xt_tgchk_param *par) } static unsigned int -tos_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct ipt_tos_target_info *info = par->targinfo; - struct iphdr *iph = ip_hdr(skb); - u_int8_t oldtos; - - if ((iph->tos & IPTOS_TOS_MASK) != info->tos) { - if (!skb_make_writable(skb, sizeof(struct iphdr))) - return NF_DROP; - - iph = ip_hdr(skb); - oldtos = iph->tos; - iph->tos = (iph->tos & IPTOS_PREC_MASK) | info->tos; - csum_replace2(&iph->check, htons(oldtos), htons(iph->tos)); - } - - return XT_CONTINUE; -} - -static bool tos_tg_check_v0(const struct xt_tgchk_param *par) -{ - const struct ipt_tos_target_info *info = par->targinfo; - const uint8_t tos = info->tos; - - if (tos != IPTOS_LOWDELAY && tos != IPTOS_THROUGHPUT && - tos != IPTOS_RELIABILITY && tos != IPTOS_MINCOST && - tos != IPTOS_NORMALSVC) { - printk(KERN_WARNING "TOS: bad tos value %#x\n", tos); - return false; - } - - return true; -} - -static unsigned int tos_tg(struct sk_buff *skb, const struct xt_target_param *par) { const struct xt_tos_target_info *info = par->targinfo; @@ -168,16 +132,6 @@ static struct xt_target dscp_tg_reg[] __read_mostly = { }, { .name = "TOS", - .revision = 0, - .family = NFPROTO_IPV4, - .table = "mangle", - .target = tos_tg_v0, - .targetsize = sizeof(struct ipt_tos_target_info), - .checkentry = tos_tg_check_v0, - .me = THIS_MODULE, - }, - { - .name = "TOS", .revision = 1, .family = NFPROTO_IPV4, .table = "mangle", diff --git a/net/netfilter/xt_dscp.c b/net/netfilter/xt_dscp.c index c3f8085..0280d3a 100644 --- a/net/netfilter/xt_dscp.c +++ b/net/netfilter/xt_dscp.c @@ -15,7 +15,6 @@ #include #include -#include MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("Xtables: DSCP/TOS field match"); @@ -55,14 +54,6 @@ static bool dscp_mt_check(const struct xt_mtchk_param *par) return true; } -static bool -tos_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct ipt_tos_info *info = par->matchinfo; - - return (ip_hdr(skb)->tos == info->tos) ^ info->invert; -} - static bool tos_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct xt_tos_match_info *info = par->matchinfo; @@ -94,14 +85,6 @@ static struct xt_match dscp_mt_reg[] __read_mostly = { }, { .name = "tos", - .revision = 0, - .family = NFPROTO_IPV4, - .match = tos_mt_v0, - .matchsize = sizeof(struct ipt_tos_info), - .me = THIS_MODULE, - }, - { - .name = "tos", .revision = 1, .family = NFPROTO_IPV4, .match = tos_mt, -- cgit v1.1 From e973a70ca033bfcd4d8b59d1f66bfc1e782e1276 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:42:12 +0200 Subject: netfilter: xtables: remove xt_CONNMARK v0 Superseded by xt_CONNMARK v1 (v2.6.24-2917-g0dc8c76). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_CONNMARK.c | 134 ++++---------------------------------------- 1 file changed, 11 insertions(+), 123 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_CONNMARK.c b/net/netfilter/xt_CONNMARK.c index d6e5ab4..5934570 100644 --- a/net/netfilter/xt_CONNMARK.c +++ b/net/netfilter/xt_CONNMARK.c @@ -36,45 +36,6 @@ MODULE_ALIAS("ip6t_CONNMARK"); #include static unsigned int -connmark_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_connmark_target_info *markinfo = par->targinfo; - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - u_int32_t diff; - u_int32_t mark; - u_int32_t newmark; - - ct = nf_ct_get(skb, &ctinfo); - if (ct) { - switch(markinfo->mode) { - case XT_CONNMARK_SET: - newmark = (ct->mark & ~markinfo->mask) | markinfo->mark; - if (newmark != ct->mark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_SAVE: - newmark = (ct->mark & ~markinfo->mask) | - (skb->mark & markinfo->mask); - if (ct->mark != newmark) { - ct->mark = newmark; - nf_conntrack_event_cache(IPCT_MARK, ct); - } - break; - case XT_CONNMARK_RESTORE: - mark = skb->mark; - diff = (ct->mark ^ mark) & markinfo->mask; - skb->mark = mark ^ diff; - break; - } - } - - return XT_CONTINUE; -} - -static unsigned int connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) { const struct xt_connmark_tginfo1 *info = par->targinfo; @@ -112,30 +73,6 @@ connmark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool connmark_tg_check_v0(const struct xt_tgchk_param *par) -{ - const struct xt_connmark_target_info *matchinfo = par->targinfo; - - if (matchinfo->mode == XT_CONNMARK_RESTORE) { - if (strcmp(par->table, "mangle") != 0) { - printk(KERN_WARNING "CONNMARK: restore can only be " - "called from \"mangle\" table, not \"%s\"\n", - par->table); - return false; - } - } - if (matchinfo->mark > 0xffffffff || matchinfo->mask > 0xffffffff) { - printk(KERN_WARNING "CONNMARK: Only supports 32bit mark\n"); - return false; - } - if (nf_ct_l3proto_try_module_get(par->family) < 0) { - printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", par->family); - return false; - } - return true; -} - static bool connmark_tg_check(const struct xt_tgchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { @@ -151,74 +88,25 @@ static void connmark_tg_destroy(const struct xt_tgdtor_param *par) nf_ct_l3proto_module_put(par->family); } -#ifdef CONFIG_COMPAT -struct compat_xt_connmark_target_info { - compat_ulong_t mark, mask; - u_int8_t mode; - u_int8_t __pad1; - u_int16_t __pad2; -}; - -static void connmark_tg_compat_from_user_v0(void *dst, void *src) -{ - const struct compat_xt_connmark_target_info *cm = src; - struct xt_connmark_target_info m = { - .mark = cm->mark, - .mask = cm->mask, - .mode = cm->mode, - }; - memcpy(dst, &m, sizeof(m)); -} - -static int connmark_tg_compat_to_user_v0(void __user *dst, void *src) -{ - const struct xt_connmark_target_info *m = src; - struct compat_xt_connmark_target_info cm = { - .mark = m->mark, - .mask = m->mask, - .mode = m->mode, - }; - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} -#endif /* CONFIG_COMPAT */ - -static struct xt_target connmark_tg_reg[] __read_mostly = { - { - .name = "CONNMARK", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_tg_check_v0, - .destroy = connmark_tg_destroy, - .target = connmark_tg_v0, - .targetsize = sizeof(struct xt_connmark_target_info), -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_connmark_target_info), - .compat_from_user = connmark_tg_compat_from_user_v0, - .compat_to_user = connmark_tg_compat_to_user_v0, -#endif - .me = THIS_MODULE - }, - { - .name = "CONNMARK", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_tg_check, - .target = connmark_tg, - .targetsize = sizeof(struct xt_connmark_tginfo1), - .destroy = connmark_tg_destroy, - .me = THIS_MODULE, - }, +static struct xt_target connmark_tg_reg __read_mostly = { + .name = "CONNMARK", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_tg_check, + .target = connmark_tg, + .targetsize = sizeof(struct xt_connmark_tginfo1), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, }; static int __init connmark_tg_init(void) { - return xt_register_targets(connmark_tg_reg, - ARRAY_SIZE(connmark_tg_reg)); + return xt_register_target(&connmark_tg_reg); } static void __exit connmark_tg_exit(void) { - xt_unregister_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg)); + xt_unregister_target(&connmark_tg_reg); } module_init(connmark_tg_init); -- cgit v1.1 From c8001f7fd5a4684280fddceed9fae9ea2e4fb521 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:47:32 +0200 Subject: netfilter: xtables: remove xt_MARK v0, v1 Superseded by xt_MARK v2 (v2.6.24-2918-ge0a812a). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_MARK.c | 163 +++--------------------------------------------- 1 file changed, 9 insertions(+), 154 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c index 67574bc..225f8d1 100644 --- a/net/netfilter/xt_MARK.c +++ b/net/netfilter/xt_MARK.c @@ -25,39 +25,6 @@ MODULE_ALIAS("ipt_MARK"); MODULE_ALIAS("ip6t_MARK"); static unsigned int -mark_tg_v0(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_mark_target_info *markinfo = par->targinfo; - - skb->mark = markinfo->mark; - return XT_CONTINUE; -} - -static unsigned int -mark_tg_v1(struct sk_buff *skb, const struct xt_target_param *par) -{ - const struct xt_mark_target_info_v1 *markinfo = par->targinfo; - int mark = 0; - - switch (markinfo->mode) { - case XT_MARK_SET: - mark = markinfo->mark; - break; - - case XT_MARK_AND: - mark = skb->mark & markinfo->mark; - break; - - case XT_MARK_OR: - mark = skb->mark | markinfo->mark; - break; - } - - skb->mark = mark; - return XT_CONTINUE; -} - -static unsigned int mark_tg(struct sk_buff *skb, const struct xt_target_param *par) { const struct xt_mark_tginfo2 *info = par->targinfo; @@ -66,135 +33,23 @@ mark_tg(struct sk_buff *skb, const struct xt_target_param *par) return XT_CONTINUE; } -static bool mark_tg_check_v0(const struct xt_tgchk_param *par) -{ - const struct xt_mark_target_info *markinfo = par->targinfo; - - if (markinfo->mark > 0xffffffff) { - printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n"); - return false; - } - return true; -} - -static bool mark_tg_check_v1(const struct xt_tgchk_param *par) -{ - const struct xt_mark_target_info_v1 *markinfo = par->targinfo; - - if (markinfo->mode != XT_MARK_SET - && markinfo->mode != XT_MARK_AND - && markinfo->mode != XT_MARK_OR) { - printk(KERN_WARNING "MARK: unknown mode %u\n", - markinfo->mode); - return false; - } - if (markinfo->mark > 0xffffffff) { - printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n"); - return false; - } - return true; -} - -#ifdef CONFIG_COMPAT -struct compat_xt_mark_target_info { - compat_ulong_t mark; -}; - -static void mark_tg_compat_from_user_v0(void *dst, void *src) -{ - const struct compat_xt_mark_target_info *cm = src; - struct xt_mark_target_info m = { - .mark = cm->mark, - }; - memcpy(dst, &m, sizeof(m)); -} - -static int mark_tg_compat_to_user_v0(void __user *dst, void *src) -{ - const struct xt_mark_target_info *m = src; - struct compat_xt_mark_target_info cm = { - .mark = m->mark, - }; - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} - -struct compat_xt_mark_target_info_v1 { - compat_ulong_t mark; - u_int8_t mode; - u_int8_t __pad1; - u_int16_t __pad2; -}; - -static void mark_tg_compat_from_user_v1(void *dst, void *src) -{ - const struct compat_xt_mark_target_info_v1 *cm = src; - struct xt_mark_target_info_v1 m = { - .mark = cm->mark, - .mode = cm->mode, - }; - memcpy(dst, &m, sizeof(m)); -} - -static int mark_tg_compat_to_user_v1(void __user *dst, void *src) -{ - const struct xt_mark_target_info_v1 *m = src; - struct compat_xt_mark_target_info_v1 cm = { - .mark = m->mark, - .mode = m->mode, - }; - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} -#endif /* CONFIG_COMPAT */ - -static struct xt_target mark_tg_reg[] __read_mostly = { - { - .name = "MARK", - .family = NFPROTO_UNSPEC, - .revision = 0, - .checkentry = mark_tg_check_v0, - .target = mark_tg_v0, - .targetsize = sizeof(struct xt_mark_target_info), -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_mark_target_info), - .compat_from_user = mark_tg_compat_from_user_v0, - .compat_to_user = mark_tg_compat_to_user_v0, -#endif - .table = "mangle", - .me = THIS_MODULE, - }, - { - .name = "MARK", - .family = NFPROTO_UNSPEC, - .revision = 1, - .checkentry = mark_tg_check_v1, - .target = mark_tg_v1, - .targetsize = sizeof(struct xt_mark_target_info_v1), -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_mark_target_info_v1), - .compat_from_user = mark_tg_compat_from_user_v1, - .compat_to_user = mark_tg_compat_to_user_v1, -#endif - .table = "mangle", - .me = THIS_MODULE, - }, - { - .name = "MARK", - .revision = 2, - .family = NFPROTO_UNSPEC, - .target = mark_tg, - .targetsize = sizeof(struct xt_mark_tginfo2), - .me = THIS_MODULE, - }, +static struct xt_target mark_tg_reg __read_mostly = { + .name = "MARK", + .revision = 2, + .family = NFPROTO_UNSPEC, + .target = mark_tg, + .targetsize = sizeof(struct xt_mark_tginfo2), + .me = THIS_MODULE, }; static int __init mark_tg_init(void) { - return xt_register_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg)); + return xt_register_target(&mark_tg_reg); } static void __exit mark_tg_exit(void) { - xt_unregister_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg)); + xt_unregister_target(&mark_tg_reg); } module_init(mark_tg_init); -- cgit v1.1 From 84899a2b9adaf6c2e20d198d7c24562ce6b391d8 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:50:33 +0200 Subject: netfilter: xtables: remove xt_connmark v0 Superseded by xt_connmark v1 (v2.6.24-2919-g96e3227). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_connmark.c | 101 +++++--------------------------------------- 1 file changed, 11 insertions(+), 90 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 86cacab..122aa8b 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -47,36 +47,6 @@ connmark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((ct->mark & info->mask) == info->mark) ^ info->invert; } -static bool -connmark_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct xt_connmark_info *info = par->matchinfo; - const struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - - ct = nf_ct_get(skb, &ctinfo); - if (!ct) - return false; - - return ((ct->mark & info->mask) == info->mark) ^ info->invert; -} - -static bool connmark_mt_check_v0(const struct xt_mtchk_param *par) -{ - const struct xt_connmark_info *cm = par->matchinfo; - - if (cm->mark > 0xffffffff || cm->mask > 0xffffffff) { - printk(KERN_WARNING "connmark: only support 32bit mark\n"); - return false; - } - if (nf_ct_l3proto_try_module_get(par->family) < 0) { - printk(KERN_WARNING "can't load conntrack support for " - "proto=%u\n", par->family); - return false; - } - return true; -} - static bool connmark_mt_check(const struct xt_mtchk_param *par) { if (nf_ct_l3proto_try_module_get(par->family) < 0) { @@ -92,74 +62,25 @@ static void connmark_mt_destroy(const struct xt_mtdtor_param *par) nf_ct_l3proto_module_put(par->family); } -#ifdef CONFIG_COMPAT -struct compat_xt_connmark_info { - compat_ulong_t mark, mask; - u_int8_t invert; - u_int8_t __pad1; - u_int16_t __pad2; -}; - -static void connmark_mt_compat_from_user_v0(void *dst, void *src) -{ - const struct compat_xt_connmark_info *cm = src; - struct xt_connmark_info m = { - .mark = cm->mark, - .mask = cm->mask, - .invert = cm->invert, - }; - memcpy(dst, &m, sizeof(m)); -} - -static int connmark_mt_compat_to_user_v0(void __user *dst, void *src) -{ - const struct xt_connmark_info *m = src; - struct compat_xt_connmark_info cm = { - .mark = m->mark, - .mask = m->mask, - .invert = m->invert, - }; - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} -#endif /* CONFIG_COMPAT */ - -static struct xt_match connmark_mt_reg[] __read_mostly = { - { - .name = "connmark", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_mt_check_v0, - .match = connmark_mt_v0, - .destroy = connmark_mt_destroy, - .matchsize = sizeof(struct xt_connmark_info), -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_connmark_info), - .compat_from_user = connmark_mt_compat_from_user_v0, - .compat_to_user = connmark_mt_compat_to_user_v0, -#endif - .me = THIS_MODULE - }, - { - .name = "connmark", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = connmark_mt_check, - .match = connmark_mt, - .matchsize = sizeof(struct xt_connmark_mtinfo1), - .destroy = connmark_mt_destroy, - .me = THIS_MODULE, - }, +static struct xt_match connmark_mt_reg __read_mostly = { + .name = "connmark", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connmark_mt_check, + .match = connmark_mt, + .matchsize = sizeof(struct xt_connmark_mtinfo1), + .destroy = connmark_mt_destroy, + .me = THIS_MODULE, }; static int __init connmark_mt_init(void) { - return xt_register_matches(connmark_mt_reg, - ARRAY_SIZE(connmark_mt_reg)); + return xt_register_match(&connmark_mt_reg); } static void __exit connmark_mt_exit(void) { - xt_unregister_matches(connmark_mt_reg, ARRAY_SIZE(connmark_mt_reg)); + xt_unregister_match(&connmark_mt_reg); } module_init(connmark_mt_init); -- cgit v1.1 From 9e05ec4b1804a1ba51f61fe169aef9b86edcd3f7 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:56:14 +0200 Subject: netfilter: xtables: remove xt_conntrack v0 Superseded by xt_conntrack v1 (v2.6.24-2921-g64eb12f). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_conntrack.c | 155 +------------------------------------------ 1 file changed, 1 insertion(+), 154 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index fc58180..6dc4652 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -19,101 +19,12 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher "); -MODULE_AUTHOR("Jan Engelhardt "); +MODULE_AUTHOR("Jan Engelhardt "); MODULE_DESCRIPTION("Xtables: connection tracking state match"); MODULE_ALIAS("ipt_conntrack"); MODULE_ALIAS("ip6t_conntrack"); static bool -conntrack_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct xt_conntrack_info *sinfo = par->matchinfo; - const struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - unsigned int statebit; - - ct = nf_ct_get(skb, &ctinfo); - -#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg))) - - if (ct == &nf_conntrack_untracked) - statebit = XT_CONNTRACK_STATE_UNTRACKED; - else if (ct) - statebit = XT_CONNTRACK_STATE_BIT(ctinfo); - else - statebit = XT_CONNTRACK_STATE_INVALID; - - if (sinfo->flags & XT_CONNTRACK_STATE) { - if (ct) { - if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_SNAT; - if (test_bit(IPS_DST_NAT_BIT, &ct->status)) - statebit |= XT_CONNTRACK_STATE_DNAT; - } - if (FWINV((statebit & sinfo->statemask) == 0, - XT_CONNTRACK_STATE)) - return false; - } - - if (ct == NULL) { - if (sinfo->flags & ~XT_CONNTRACK_STATE) - return false; - return true; - } - - if (sinfo->flags & XT_CONNTRACK_PROTO && - FWINV(nf_ct_protonum(ct) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, - XT_CONNTRACK_PROTO)) - return false; - - if (sinfo->flags & XT_CONNTRACK_ORIGSRC && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & - sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, - XT_CONNTRACK_ORIGSRC)) - return false; - - if (sinfo->flags & XT_CONNTRACK_ORIGDST && - FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & - sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != - sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, - XT_CONNTRACK_ORIGDST)) - return false; - - if (sinfo->flags & XT_CONNTRACK_REPLSRC && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & - sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].src.ip, - XT_CONNTRACK_REPLSRC)) - return false; - - if (sinfo->flags & XT_CONNTRACK_REPLDST && - FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & - sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != - sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, - XT_CONNTRACK_REPLDST)) - return false; - - if (sinfo->flags & XT_CONNTRACK_STATUS && - FWINV((ct->status & sinfo->statusmask) == 0, - XT_CONNTRACK_STATUS)) - return false; - - if(sinfo->flags & XT_CONNTRACK_EXPIRES) { - unsigned long expires = timer_pending(&ct->timeout) ? - (ct->timeout.expires - jiffies)/HZ : 0; - - if (FWINV(!(expires >= sinfo->expires_min && - expires <= sinfo->expires_max), - XT_CONNTRACK_EXPIRES)) - return false; - } - return true; -#undef FWINV -} - -static bool conntrack_addrcmp(const union nf_inet_addr *kaddr, const union nf_inet_addr *uaddr, const union nf_inet_addr *umask, unsigned int l3proto) @@ -337,73 +248,9 @@ static void conntrack_mt_destroy_v1(const struct xt_mtdtor_param *par) conntrack_mt_destroy(par); } -#ifdef CONFIG_COMPAT -struct compat_xt_conntrack_info -{ - compat_uint_t statemask; - compat_uint_t statusmask; - struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; - struct in_addr sipmsk[IP_CT_DIR_MAX]; - struct in_addr dipmsk[IP_CT_DIR_MAX]; - compat_ulong_t expires_min; - compat_ulong_t expires_max; - u_int8_t flags; - u_int8_t invflags; -}; - -static void conntrack_mt_compat_from_user_v0(void *dst, void *src) -{ - const struct compat_xt_conntrack_info *cm = src; - struct xt_conntrack_info m = { - .statemask = cm->statemask, - .statusmask = cm->statusmask, - .expires_min = cm->expires_min, - .expires_max = cm->expires_max, - .flags = cm->flags, - .invflags = cm->invflags, - }; - memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); - memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); - memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); - memcpy(dst, &m, sizeof(m)); -} - -static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) -{ - const struct xt_conntrack_info *m = src; - struct compat_xt_conntrack_info cm = { - .statemask = m->statemask, - .statusmask = m->statusmask, - .expires_min = m->expires_min, - .expires_max = m->expires_max, - .flags = m->flags, - .invflags = m->invflags, - }; - memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); - memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); - memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} -#endif - static struct xt_match conntrack_mt_reg[] __read_mostly = { { .name = "conntrack", - .revision = 0, - .family = NFPROTO_IPV4, - .match = conntrack_mt_v0, - .checkentry = conntrack_mt_check, - .destroy = conntrack_mt_destroy, - .matchsize = sizeof(struct xt_conntrack_info), - .me = THIS_MODULE, -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_conntrack_info), - .compat_from_user = conntrack_mt_compat_from_user_v0, - .compat_to_user = conntrack_mt_compat_to_user_v0, -#endif - }, - { - .name = "conntrack", .revision = 1, .family = NFPROTO_UNSPEC, .matchsize = sizeof(struct xt_conntrack_mtinfo1), -- cgit v1.1 From 36d4084dc8eb7a9a3655a2041097a46aff3061e9 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 18:58:19 +0200 Subject: netfilter: xtables: remove xt_iprange v0 Superseded by xt_iprange v1 (v2.6.24-2928-g1a50c5a1). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_iprange.c | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index 501f9b6..ffc9638 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -14,40 +14,6 @@ #include #include #include -#include - -static bool -iprange_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct ipt_iprange_info *info = par->matchinfo; - const struct iphdr *iph = ip_hdr(skb); - - if (info->flags & IPRANGE_SRC) { - if ((ntohl(iph->saddr) < ntohl(info->src.min_ip) - || ntohl(iph->saddr) > ntohl(info->src.max_ip)) - ^ !!(info->flags & IPRANGE_SRC_INV)) { - pr_debug("src IP %pI4 NOT in range %s%pI4-%pI4\n", - &iph->saddr, - info->flags & IPRANGE_SRC_INV ? "(INV) " : "", - &info->src.min_ip, - &info->src.max_ip); - return false; - } - } - if (info->flags & IPRANGE_DST) { - if ((ntohl(iph->daddr) < ntohl(info->dst.min_ip) - || ntohl(iph->daddr) > ntohl(info->dst.max_ip)) - ^ !!(info->flags & IPRANGE_DST_INV)) { - pr_debug("dst IP %pI4 NOT in range %s%pI4-%pI4\n", - &iph->daddr, - info->flags & IPRANGE_DST_INV ? "(INV) " : "", - &info->dst.min_ip, - &info->dst.max_ip); - return false; - } - } - return true; -} static bool iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par) @@ -127,14 +93,6 @@ iprange_mt6(const struct sk_buff *skb, const struct xt_match_param *par) static struct xt_match iprange_mt_reg[] __read_mostly = { { .name = "iprange", - .revision = 0, - .family = NFPROTO_IPV4, - .match = iprange_mt_v0, - .matchsize = sizeof(struct ipt_iprange_info), - .me = THIS_MODULE, - }, - { - .name = "iprange", .revision = 1, .family = NFPROTO_IPV4, .match = iprange_mt4, @@ -164,7 +122,8 @@ static void __exit iprange_mt_exit(void) module_init(iprange_mt_init); module_exit(iprange_mt_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik , Jan Engelhardt "); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_AUTHOR("Jan Engelhardt "); MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching"); MODULE_ALIAS("ipt_iprange"); MODULE_ALIAS("ip6t_iprange"); -- cgit v1.1 From 4725c7287ef2c4340cb433f59e40d143c1f43c22 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 19:02:27 +0200 Subject: netfilter: xtables: remove xt_mark v0 Superseded by xt_mark v1 (v2.6.24-2922-g17b0d7e). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_mark.c | 86 ++++++------------------------------------------- 1 file changed, 10 insertions(+), 76 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 10b9e34..1db07d8 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -3,7 +3,7 @@ * * (C) 1999-2001 Marc Boucher * Copyright © CC Computer Consultants GmbH, 2007 - 2008 - * Jan Engelhardt + * Jan Engelhardt * * 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 @@ -23,14 +23,6 @@ MODULE_ALIAS("ipt_mark"); MODULE_ALIAS("ip6t_mark"); static bool -mark_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct xt_mark_info *info = par->matchinfo; - - return ((skb->mark & info->mask) == info->mark) ^ info->invert; -} - -static bool mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) { const struct xt_mark_mtinfo1 *info = par->matchinfo; @@ -38,81 +30,23 @@ mark_mt(const struct sk_buff *skb, const struct xt_match_param *par) return ((skb->mark & info->mask) == info->mark) ^ info->invert; } -static bool mark_mt_check_v0(const struct xt_mtchk_param *par) -{ - const struct xt_mark_info *minfo = par->matchinfo; - - if (minfo->mark > 0xffffffff || minfo->mask > 0xffffffff) { - printk(KERN_WARNING "mark: only supports 32bit mark\n"); - return false; - } - return true; -} - -#ifdef CONFIG_COMPAT -struct compat_xt_mark_info { - compat_ulong_t mark, mask; - u_int8_t invert; - u_int8_t __pad1; - u_int16_t __pad2; -}; - -static void mark_mt_compat_from_user_v0(void *dst, void *src) -{ - const struct compat_xt_mark_info *cm = src; - struct xt_mark_info m = { - .mark = cm->mark, - .mask = cm->mask, - .invert = cm->invert, - }; - memcpy(dst, &m, sizeof(m)); -} - -static int mark_mt_compat_to_user_v0(void __user *dst, void *src) -{ - const struct xt_mark_info *m = src; - struct compat_xt_mark_info cm = { - .mark = m->mark, - .mask = m->mask, - .invert = m->invert, - }; - return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; -} -#endif /* CONFIG_COMPAT */ - -static struct xt_match mark_mt_reg[] __read_mostly = { - { - .name = "mark", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = mark_mt_check_v0, - .match = mark_mt_v0, - .matchsize = sizeof(struct xt_mark_info), -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_xt_mark_info), - .compat_from_user = mark_mt_compat_from_user_v0, - .compat_to_user = mark_mt_compat_to_user_v0, -#endif - .me = THIS_MODULE, - }, - { - .name = "mark", - .revision = 1, - .family = NFPROTO_UNSPEC, - .match = mark_mt, - .matchsize = sizeof(struct xt_mark_mtinfo1), - .me = THIS_MODULE, - }, +static struct xt_match mark_mt_reg __read_mostly = { + .name = "mark", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = mark_mt, + .matchsize = sizeof(struct xt_mark_mtinfo1), + .me = THIS_MODULE, }; static int __init mark_mt_init(void) { - return xt_register_matches(mark_mt_reg, ARRAY_SIZE(mark_mt_reg)); + return xt_register_match(&mark_mt_reg); } static void __exit mark_mt_exit(void) { - xt_unregister_matches(mark_mt_reg, ARRAY_SIZE(mark_mt_reg)); + xt_unregister_match(&mark_mt_reg); } module_init(mark_mt_init); -- cgit v1.1 From 6461caed83412ae3e9a16785ffa64396fb66c6a6 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 12 Jun 2009 19:46:26 +0200 Subject: netfilter: xtables: remove xt_owner v0 Superseded by xt_owner v1 (v2.6.24-2388-g0265ab4). Signed-off-by: Jan Engelhardt --- net/netfilter/xt_owner.c | 130 +++++------------------------------------------ 1 file changed, 12 insertions(+), 118 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index 22b2a5e..d24c76d 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -5,7 +5,6 @@ * (C) 2000 Marc Boucher * * Copyright © CC Computer Consultants GmbH, 2007 - 2008 - * * * 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 @@ -17,60 +16,6 @@ #include #include #include -#include -#include - -static bool -owner_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct ipt_owner_info *info = par->matchinfo; - const struct file *filp; - - if (skb->sk == NULL || skb->sk->sk_socket == NULL) - return false; - - filp = skb->sk->sk_socket->file; - if (filp == NULL) - return false; - - if (info->match & IPT_OWNER_UID) - if ((filp->f_cred->fsuid != info->uid) ^ - !!(info->invert & IPT_OWNER_UID)) - return false; - - if (info->match & IPT_OWNER_GID) - if ((filp->f_cred->fsgid != info->gid) ^ - !!(info->invert & IPT_OWNER_GID)) - return false; - - return true; -} - -static bool -owner_mt6_v0(const struct sk_buff *skb, const struct xt_match_param *par) -{ - const struct ip6t_owner_info *info = par->matchinfo; - const struct file *filp; - - if (skb->sk == NULL || skb->sk->sk_socket == NULL) - return false; - - filp = skb->sk->sk_socket->file; - if (filp == NULL) - return false; - - if (info->match & IP6T_OWNER_UID) - if ((filp->f_cred->fsuid != info->uid) ^ - !!(info->invert & IP6T_OWNER_UID)) - return false; - - if (info->match & IP6T_OWNER_GID) - if ((filp->f_cred->fsgid != info->gid) ^ - !!(info->invert & IP6T_OWNER_GID)) - return false; - - return true; -} static bool owner_mt(const struct sk_buff *skb, const struct xt_match_param *par) @@ -107,81 +52,30 @@ owner_mt(const struct sk_buff *skb, const struct xt_match_param *par) return true; } -static bool owner_mt_check_v0(const struct xt_mtchk_param *par) -{ - const struct ipt_owner_info *info = par->matchinfo; - - if (info->match & (IPT_OWNER_PID | IPT_OWNER_SID | IPT_OWNER_COMM)) { - printk(KERN_WARNING KBUILD_MODNAME - ": PID, SID and command matching is not " - "supported anymore\n"); - return false; - } - - return true; -} - -static bool owner_mt6_check_v0(const struct xt_mtchk_param *par) -{ - const struct ip6t_owner_info *info = par->matchinfo; - - if (info->match & (IP6T_OWNER_PID | IP6T_OWNER_SID)) { - printk(KERN_WARNING KBUILD_MODNAME - ": PID and SID matching is not supported anymore\n"); - return false; - } - - return true; -} - -static struct xt_match owner_mt_reg[] __read_mostly = { - { - .name = "owner", - .revision = 0, - .family = NFPROTO_IPV4, - .match = owner_mt_v0, - .matchsize = sizeof(struct ipt_owner_info), - .checkentry = owner_mt_check_v0, - .hooks = (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING), - .me = THIS_MODULE, - }, - { - .name = "owner", - .revision = 0, - .family = NFPROTO_IPV6, - .match = owner_mt6_v0, - .matchsize = sizeof(struct ip6t_owner_info), - .checkentry = owner_mt6_check_v0, - .hooks = (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING), - .me = THIS_MODULE, - }, - { - .name = "owner", - .revision = 1, - .family = NFPROTO_UNSPEC, - .match = owner_mt, - .matchsize = sizeof(struct xt_owner_match_info), - .hooks = (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING), - .me = THIS_MODULE, - }, +static struct xt_match owner_mt_reg __read_mostly = { + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = owner_mt, + .matchsize = sizeof(struct xt_owner_match_info), + .hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING), + .me = THIS_MODULE, }; static int __init owner_mt_init(void) { - return xt_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); + return xt_register_match(&owner_mt_reg); } static void __exit owner_mt_exit(void) { - xt_unregister_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); + xt_unregister_match(&owner_mt_reg); } module_init(owner_mt_init); module_exit(owner_mt_exit); -MODULE_AUTHOR("Jan Engelhardt "); +MODULE_AUTHOR("Jan Engelhardt "); MODULE_DESCRIPTION("Xtables: socket owner matching"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_owner"); -- cgit v1.1 From 57750a22ed022ed6fcdcc5bc58d16011ccfe575f Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 13 Jun 2009 06:22:18 +0200 Subject: netfilter: conntrack: switch hook PFs to nfproto Simple substitution to indicate that the fields indeed use the NFPROTO_ space. Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 8 ++++---- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 7d2ead7..8905cc7 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -158,28 +158,28 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { { .hook = ipv4_conntrack_in, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_CONNTRACK, }, { .hook = ipv4_conntrack_local, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_CONNTRACK, }, { .hook = ipv4_confirm, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_CONNTRACK_CONFIRM, }, { .hook = ipv4_confirm, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_CONNTRACK_CONFIRM, }, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 2a15c2d..a7f4cd6 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -265,42 +265,42 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { { .hook = ipv6_defrag, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, }, { .hook = ipv6_conntrack_in, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP6_PRI_CONNTRACK, }, { .hook = ipv6_conntrack_local, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_CONNTRACK, }, { .hook = ipv6_defrag, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, }, { .hook = ipv6_confirm, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_LAST, }, { .hook = ipv6_confirm, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_LAST-1, }, -- cgit v1.1 From 24c232d8e911ef6189e02da411dc2b72cb03bfcf Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 13 Jun 2009 06:20:29 +0200 Subject: netfilter: xtables: switch hook PFs to nfproto Signed-off-by: Jan Engelhardt --- net/bridge/netfilter/ebtable_filter.c | 6 +++--- net/bridge/netfilter/ebtable_nat.c | 6 +++--- net/ipv4/netfilter/iptable_filter.c | 6 +++--- net/ipv4/netfilter/iptable_mangle.c | 10 +++++----- net/ipv4/netfilter/iptable_raw.c | 4 ++-- net/ipv4/netfilter/iptable_security.c | 6 +++--- net/ipv4/netfilter/nf_nat_standalone.c | 8 ++++---- net/ipv6/netfilter/ip6table_filter.c | 6 +++--- net/ipv6/netfilter/ip6table_mangle.c | 10 +++++----- net/ipv6/netfilter/ip6table_raw.c | 4 ++-- net/ipv6/netfilter/ip6table_security.c | 6 +++--- 11 files changed, 36 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index a5eea72..4b988db 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -77,21 +77,21 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { { .hook = ebt_in_hook, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_LOCAL_IN, .priority = NF_BR_PRI_FILTER_BRIDGED, }, { .hook = ebt_in_hook, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_FORWARD, .priority = NF_BR_PRI_FILTER_BRIDGED, }, { .hook = ebt_out_hook, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_LOCAL_OUT, .priority = NF_BR_PRI_FILTER_OTHER, }, diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 6024c55..4a98804 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -77,21 +77,21 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { { .hook = ebt_nat_out, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_LOCAL_OUT, .priority = NF_BR_PRI_NAT_DST_OTHER, }, { .hook = ebt_nat_out, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_POST_ROUTING, .priority = NF_BR_PRI_NAT_SRC, }, { .hook = ebt_nat_in, .owner = THIS_MODULE, - .pf = PF_BRIDGE, + .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_PRE_ROUTING, .priority = NF_BR_PRI_NAT_DST_BRIDGED, }, diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index c30a969..bef326c 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -102,21 +102,21 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = { { .hook = ipt_local_in_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_FILTER, }, { .hook = ipt_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_FILTER, }, { .hook = ipt_local_out_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_FILTER, }, diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 4087614..1442df7 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -162,35 +162,35 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = { { .hook = ipt_pre_routing_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_MANGLE, }, { .hook = ipt_local_in_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_MANGLE, }, { .hook = ipt_forward_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_MANGLE, }, { .hook = ipt_local_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_MANGLE, }, { .hook = ipt_post_routing_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_MANGLE, }, diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index e5356da..1d28df8 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -74,14 +74,14 @@ ipt_local_hook(unsigned int hook, static struct nf_hook_ops ipt_ops[] __read_mostly = { { .hook = ipt_hook, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_RAW, .owner = THIS_MODULE, }, { .hook = ipt_local_hook, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_RAW, .owner = THIS_MODULE, diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index 29ab630..8c5a250 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -105,21 +105,21 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = { { .hook = ipt_local_in_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_SECURITY, }, { .hook = ipt_forward_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_SECURITY, }, { .hook = ipt_local_out_hook, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SECURITY, }, diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 5567bd0..5f41d01 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -251,7 +251,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = { { .hook = nf_nat_in, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_NAT_DST, }, @@ -259,7 +259,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = { { .hook = nf_nat_out, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_NAT_SRC, }, @@ -267,7 +267,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = { { .hook = nf_nat_local_fn, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_NAT_DST, }, @@ -275,7 +275,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = { { .hook = nf_nat_fn, .owner = THIS_MODULE, - .pf = PF_INET, + .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_NAT_SRC, }, diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index ef5a0a3..b35c358 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -95,21 +95,21 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = { { .hook = ip6t_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_FILTER, }, { .hook = ip6t_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_FILTER, }, { .hook = ip6t_local_out_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_FILTER, }, diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index ab0d398..a98ced1 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -136,35 +136,35 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = { { .hook = ip6t_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP6_PRI_MANGLE, }, { .hook = ip6t_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_MANGLE, }, { .hook = ip6t_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_MANGLE, }, { .hook = ip6t_local_out_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_MANGLE, }, { .hook = ip6t_post_routing_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_MANGLE, }, diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 4b792b6..ec12540 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -68,14 +68,14 @@ ip6t_local_out_hook(unsigned int hook, static struct nf_hook_ops ip6t_ops[] __read_mostly = { { .hook = ip6t_pre_routing_hook, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP6_PRI_FIRST, .owner = THIS_MODULE, }, { .hook = ip6t_local_out_hook, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_FIRST, .owner = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 0ea37ff..d808507 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -101,21 +101,21 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = { { .hook = ip6t_local_in_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_SECURITY, }, { .hook = ip6t_forward_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_SECURITY, }, { .hook = ip6t_local_out_hook, .owner = THIS_MODULE, - .pf = PF_INET6, + .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_SECURITY, }, -- cgit v1.1 From f88e6a8a50a603f8347343e75d035889784a507c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 13 Jun 2009 06:25:44 +0200 Subject: netfilter: xtables: switch table AFs to nfproto Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 2 +- net/ipv4/netfilter/iptable_raw.c | 2 +- net/ipv4/netfilter/iptable_security.c | 2 +- net/ipv4/netfilter/nf_nat_rule.c | 2 +- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 2 +- net/ipv6/netfilter/ip6table_raw.c | 2 +- net/ipv6/netfilter/ip6table_security.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index bef326c..97dbd94 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -57,7 +57,7 @@ static struct xt_table packet_filter = { .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET, + .af = NFPROTO_IPV4, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 1442df7..28647f1 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -68,7 +68,7 @@ static struct xt_table packet_mangler = { .name = "mangle", .valid_hooks = MANGLE_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET, + .af = NFPROTO_IPV4, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 1d28df8..494784c 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -40,7 +40,7 @@ static struct xt_table packet_raw = { .name = "raw", .valid_hooks = RAW_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET, + .af = NFPROTO_IPV4, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index 8c5a250..8804e1a 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -61,7 +61,7 @@ static struct xt_table security_table = { .name = "security", .valid_hooks = SECURITY_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET, + .af = NFPROTO_IPV4, }; static unsigned int diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 6348a79..6448a9b 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -62,7 +62,7 @@ static struct xt_table nat_table = { .name = "nat", .valid_hooks = NAT_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET, + .af = NFPROTO_IPV4, }; /* Source NAT */ diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index b35c358..0a3ae48 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -55,7 +55,7 @@ static struct xt_table packet_filter = { .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET6, + .af = NFPROTO_IPV6, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index a98ced1..0f49e00 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -61,7 +61,7 @@ static struct xt_table packet_mangler = { .name = "mangle", .valid_hooks = MANGLE_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET6, + .af = NFPROTO_IPV6, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index ec12540..679865e 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -39,7 +39,7 @@ static struct xt_table packet_raw = { .name = "raw", .valid_hooks = RAW_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET6, + .af = NFPROTO_IPV6, }; /* The work comes in here from netfilter.c. */ diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index d808507..822afab 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -60,7 +60,7 @@ static struct xt_table security_table = { .name = "security", .valid_hooks = SECURITY_VALID_HOOKS, .me = THIS_MODULE, - .af = AF_INET6, + .af = NFPROTO_IPV6, }; static unsigned int -- cgit v1.1 From e5afbba1869a5d9509c61f8962be9bdebf95f7d3 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 8 Jul 2009 12:33:10 +0200 Subject: netfilter: iptables: remove unused datalen variable Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/ip_tables.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index fdefae6..3856aa3 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -315,7 +315,6 @@ ipt_do_table(struct sk_buff *skb, static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); const struct iphdr *ip; - u_int16_t datalen; bool hotdrop = false; /* Initializing verdict to NF_DROP keeps gcc happy. */ unsigned int verdict = NF_DROP; @@ -328,7 +327,6 @@ ipt_do_table(struct sk_buff *skb, /* Initialization */ ip = ip_hdr(skb); - datalen = skb->len - ip->ihl * 4; indev = in ? in->name : nulldevname; outdev = out ? out->name : nulldevname; /* We handle fragments by dealing with the first fragment as @@ -427,8 +425,6 @@ ipt_do_table(struct sk_buff *skb, #endif /* Target might have changed stuff. */ ip = ip_hdr(skb); - datalen = skb->len - ip->ihl * 4; - if (verdict == IPT_CONTINUE) e = ipt_next_entry(e); else -- cgit v1.1 From 47901dc2c4a3f1f9af453486a005d31fe9b393f0 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 9 Jul 2009 23:00:19 +0200 Subject: netfilter: xtables: use memcmp in unconditional check Instead of inspecting each u32/char open-coded, clean up and make use of memcmp. On some arches, memcmp is implemented as assembly or GCC's __builtin_memcmp which can possibly take advantages of known alignment. Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/arp_tables.c | 10 +++------- net/ipv4/netfilter/ip_tables.c | 11 +++-------- net/ipv6/netfilter/ip6_tables.c | 11 +++-------- 3 files changed, 9 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 7505dff..b9f7243 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -341,15 +341,11 @@ unsigned int arpt_do_table(struct sk_buff *skb, } /* All zeroes == unconditional rule. */ -static inline int unconditional(const struct arpt_arp *arp) +static inline bool unconditional(const struct arpt_arp *arp) { - unsigned int i; - - for (i = 0; i < sizeof(*arp)/sizeof(__u32); i++) - if (((__u32 *)arp)[i]) - return 0; + static const struct arpt_arp uncond; - return 1; + return memcmp(arp, &uncond, sizeof(uncond)) == 0; } /* Figures out from what hook each rule can be called: returns 0 if diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 3856aa3..3431a77 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -190,16 +190,11 @@ get_entry(void *base, unsigned int offset) /* All zeroes == unconditional rule. */ /* Mildly perf critical (only if packet tracing is on) */ -static inline int -unconditional(const struct ipt_ip *ip) +static inline bool unconditional(const struct ipt_ip *ip) { - unsigned int i; + static const struct ipt_ip uncond; - for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++) - if (((__u32 *)ip)[i]) - return 0; - - return 1; + return memcmp(ip, &uncond, sizeof(uncond)) == 0; #undef FWINV } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index ced1f2c..1389ad9 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -222,16 +222,11 @@ get_entry(void *base, unsigned int offset) /* All zeroes == unconditional rule. */ /* Mildly perf critical (only if packet tracing is on) */ -static inline int -unconditional(const struct ip6t_ip6 *ipv6) +static inline bool unconditional(const struct ip6t_ip6 *ipv6) { - unsigned int i; - - for (i = 0; i < sizeof(*ipv6); i++) - if (((char *)ipv6)[i]) - break; + static const struct ip6t_ip6 uncond; - return (i == sizeof(*ipv6)); + return memcmp(ipv6, &uncond, sizeof(uncond)) == 0; } #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ -- cgit v1.1 From a7d51738e757c1ab94595e7d05594c61f0fb32ce Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 18 Jul 2009 14:52:58 +0200 Subject: netfilter: xtables: ignore unassigned hooks in check_entry_size_and_hooks The "hook_entry" and "underflow" array contains values even for hooks not provided, such as PREROUTING in conjunction with the "filter" table. Usually, the values point to whatever the next rule is. For the upcoming unconditionality and underflow checking patches however, we must not inspect that arbitrary rule. Skipping unassigned hooks seems like a good idea, also because newinfo->hook_entry and newinfo->underflow will then continue to have the poison value for detecting abnormalities. Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/arp_tables.c | 5 ++++- net/ipv4/netfilter/ip_tables.c | 5 ++++- net/ipv6/netfilter/ip6_tables.c | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index b9f7243..d91f083 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -539,6 +539,7 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, unsigned char *limit, const unsigned int *hook_entries, const unsigned int *underflows, + unsigned int valid_hooks, unsigned int *i) { unsigned int h; @@ -558,6 +559,8 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, /* Check hooks & underflows */ for (h = 0; h < NF_ARP_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) + continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) @@ -626,7 +629,7 @@ static int translate_table(const char *name, newinfo, entry0, entry0 + size, - hook_entries, underflows, &i); + hook_entries, underflows, valid_hooks, &i); duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret); if (ret != 0) return ret; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 3431a77..6e7b7e8 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -714,6 +714,7 @@ check_entry_size_and_hooks(struct ipt_entry *e, unsigned char *limit, const unsigned int *hook_entries, const unsigned int *underflows, + unsigned int valid_hooks, unsigned int *i) { unsigned int h; @@ -733,6 +734,8 @@ check_entry_size_and_hooks(struct ipt_entry *e, /* Check hooks & underflows */ for (h = 0; h < NF_INET_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) + continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) @@ -804,7 +807,7 @@ translate_table(const char *name, newinfo, entry0, entry0 + size, - hook_entries, underflows, &i); + hook_entries, underflows, valid_hooks, &i); if (ret != 0) return ret; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 1389ad9..8e4921a 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -747,6 +747,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e, unsigned char *limit, const unsigned int *hook_entries, const unsigned int *underflows, + unsigned int valid_hooks, unsigned int *i) { unsigned int h; @@ -766,6 +767,8 @@ check_entry_size_and_hooks(struct ip6t_entry *e, /* Check hooks & underflows */ for (h = 0; h < NF_INET_NUMHOOKS; h++) { + if (!(valid_hooks & (1 << h))) + continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) @@ -837,7 +840,7 @@ translate_table(const char *name, newinfo, entry0, entry0 + size, - hook_entries, underflows, &i); + hook_entries, underflows, valid_hooks, &i); if (ret != 0) return ret; -- cgit v1.1 From 90e7d4ab5c8b0c4c2e00e4893977f6aeec0f18f1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 9 Jul 2009 22:54:53 +0200 Subject: netfilter: xtables: check for unconditionality of policies This adds a check that iptables's original author Rusty set forth in a FIXME comment. Underflows in iptables are better known as chain policies, and are required to be unconditional or there would be a stochastical chance for the policy rule to be skipped if it does not match. If that were to happen, rule execution would continue in an unexpected spurious fashion. Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/arp_tables.c | 12 +++++++----- net/ipv4/netfilter/ip_tables.c | 11 +++++++---- net/ipv6/netfilter/ip6_tables.c | 12 +++++++----- 3 files changed, 21 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index d91f083..064082d 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -8,7 +8,7 @@ * Copyright (C) 2002 David S. Miller (davem@redhat.com) * */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include @@ -563,13 +563,15 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; - if ((unsigned char *)e - base == underflows[h]) + if ((unsigned char *)e - base == underflows[h]) { + if (!unconditional(&e->arp)) { + pr_err("Underflows must be unconditional\n"); + return -EINVAL; + } newinfo->underflow[h] = underflows[h]; + } } - /* FIXME: underflows must be unconditional, standard verdicts - < 0 (not ARPT_RETURN). --RR */ - /* Clear counters and comefrom */ e->counters = ((struct xt_counters) { 0, 0 }); e->comefrom = 0; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 6e7b7e8..6e546d5 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -8,6 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include @@ -738,13 +739,15 @@ check_entry_size_and_hooks(struct ipt_entry *e, continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; - if ((unsigned char *)e - base == underflows[h]) + if ((unsigned char *)e - base == underflows[h]) { + if (!unconditional(&e->ip)) { + pr_err("Underflows must be unconditional\n"); + return -EINVAL; + } newinfo->underflow[h] = underflows[h]; + } } - /* FIXME: underflows must be unconditional, standard verdicts - < 0 (not IPT_RETURN). --RR */ - /* Clear counters and comefrom */ e->counters = ((struct xt_counters) { 0, 0 }); e->comefrom = 0; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 8e4921a..b0599b9 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -8,7 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include @@ -771,13 +771,15 @@ check_entry_size_and_hooks(struct ip6t_entry *e, continue; if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; - if ((unsigned char *)e - base == underflows[h]) + if ((unsigned char *)e - base == underflows[h]) { + if (!unconditional(&e->ipv6)) { + pr_err("Underflows must be unconditional\n"); + return -EINVAL; + } newinfo->underflow[h] = underflows[h]; + } } - /* FIXME: underflows must be unconditional, standard verdicts - < 0 (not IP6T_RETURN). --RR */ - /* Clear counters and comefrom */ e->counters = ((struct xt_counters) { 0, 0 }); e->comefrom = 0; -- cgit v1.1 From e2fe35c17fed62d4ab5038fa9bc489e967ff8416 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 18 Jul 2009 15:22:30 +0200 Subject: netfilter: xtables: check for standard verdicts in policies This adds the second check that Rusty wanted to have a long time ago. :-) Base chain policies must have absolute verdicts that cease processing in the table, otherwise rule execution may continue in an unexpected spurious fashion (e.g. next chain that follows in memory). Signed-off-by: Jan Engelhardt --- net/ipv4/netfilter/arp_tables.c | 21 +++++++++++++++++++-- net/ipv4/netfilter/ip_tables.c | 21 +++++++++++++++++++-- net/ipv6/netfilter/ip6_tables.c | 21 +++++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 064082d..7bc11ff 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -533,6 +533,21 @@ out: return ret; } +static bool check_underflow(struct arpt_entry *e) +{ + const struct arpt_entry_target *t; + unsigned int verdict; + + if (!unconditional(&e->arp)) + return false; + t = arpt_get_target(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) + return false; + verdict = ((struct arpt_standard_target *)t)->verdict; + verdict = -verdict - 1; + return verdict == NF_DROP || verdict == NF_ACCEPT; +} + static inline int check_entry_size_and_hooks(struct arpt_entry *e, struct xt_table_info *newinfo, unsigned char *base, @@ -564,8 +579,10 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { - if (!unconditional(&e->arp)) { - pr_err("Underflows must be unconditional\n"); + if (!check_underflow(e)) { + pr_err("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 6e546d5..0b43fd7 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -708,6 +708,21 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size, return ret; } +static bool check_underflow(struct ipt_entry *e) +{ + const struct ipt_entry_target *t; + unsigned int verdict; + + if (!unconditional(&e->ip)) + return false; + t = ipt_get_target(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) + return false; + verdict = ((struct ipt_standard_target *)t)->verdict; + verdict = -verdict - 1; + return verdict == NF_DROP || verdict == NF_ACCEPT; +} + static int check_entry_size_and_hooks(struct ipt_entry *e, struct xt_table_info *newinfo, @@ -740,8 +755,10 @@ check_entry_size_and_hooks(struct ipt_entry *e, if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { - if (!unconditional(&e->ip)) { - pr_err("Underflows must be unconditional\n"); + if (!check_underflow(e)) { + pr_err("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index b0599b9..a5d0c27 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -740,6 +740,21 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size, return ret; } +static bool check_underflow(struct ip6t_entry *e) +{ + const struct ip6t_entry_target *t; + unsigned int verdict; + + if (!unconditional(&e->ipv6)) + return false; + t = ip6t_get_target(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) + return false; + verdict = ((struct ip6t_standard_target *)t)->verdict; + verdict = -verdict - 1; + return verdict == NF_DROP || verdict == NF_ACCEPT; +} + static int check_entry_size_and_hooks(struct ip6t_entry *e, struct xt_table_info *newinfo, @@ -772,8 +787,10 @@ check_entry_size_and_hooks(struct ip6t_entry *e, if ((unsigned char *)e - base == hook_entries[h]) newinfo->hook_entry[h] = hook_entries[h]; if ((unsigned char *)e - base == underflows[h]) { - if (!unconditional(&e->ipv6)) { - pr_err("Underflows must be unconditional\n"); + if (!check_underflow(e)) { + pr_err("Underflows must be unconditional and " + "use the STANDARD target with " + "ACCEPT/DROP\n"); return -EINVAL; } newinfo->underflow[h] = underflows[h]; -- cgit v1.1 From 6885ffb3a1b4abf731fd0891a2c1544a83c2651d Mon Sep 17 00:00:00 2001 From: Mark Smith Date: Thu, 6 Aug 2009 23:21:22 +0000 Subject: Use correct NET_RX_* returns for atalk_rcv() In all rx'd SKB cases, atalk_rcv() either eventually jumps to or falls through to the label out:, which returns numeric 0. Numeric 0 corresponds to NET_RX_SUCCESS, which is incorrect in failed SKB cases. This patch makes atalk_rcv() provide the correct returns by: o explicitly returning NET_RX_SUCCESS in the two success cases o having the out: label return NET_RX_DROP, instead of numeric 0 o making the failed SKB labels and processing more consistent with other _rcv() routines in the kernel, simplifying validation and removing a backwards goto Signed-off-by: Mark Smith Signed-off-by: David S. Miller --- net/appletalk/ddp.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 875eda5..0d42d5d 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1400,7 +1400,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, __u16 len_hops; if (!net_eq(dev_net(dev), &init_net)) - goto freeit; + goto drop; /* Don't mangle buffer if shared */ if (!(skb = skb_share_check(skb, GFP_ATOMIC))) @@ -1408,7 +1408,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, /* Size check and make sure header is contiguous */ if (!pskb_may_pull(skb, sizeof(*ddp))) - goto freeit; + goto drop; ddp = ddp_hdr(skb); @@ -1426,7 +1426,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " "skb->len=%u)\n", len_hops & 1023, skb->len); - goto freeit; + goto drop; } /* @@ -1436,7 +1436,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, if (ddp->deh_sum && atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) /* Not a valid AppleTalk frame - dustbin time */ - goto freeit; + goto drop; /* Check the packet is aimed at us */ if (!ddp->deh_dnet) /* Net 0 is 'this network' */ @@ -1449,7 +1449,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, * AppleTalk iface */ atalk_route_packet(skb, dev, ddp, len_hops, origlen); - goto out; + return NET_RX_SUCCESS; } /* if IP over DDP is not selected this code will be optimized out */ @@ -1465,18 +1465,21 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, sock = atalk_search_socket(&tosat, atif); if (!sock) /* But not one of our sockets */ - goto freeit; + goto drop; /* Queue packet (standard) */ skb->sk = sock; if (sock_queue_rcv_skb(sock, skb) < 0) - goto freeit; -out: - return 0; -freeit: + goto drop; + + return NET_RX_SUCCESS; + +drop: kfree_skb(skb); - goto out; +out: + return NET_RX_DROP; + } /* -- cgit v1.1 From 78090a58c49f2f6213d0bb1b3b4c4df73e26865f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Aug 2009 02:58:38 +0000 Subject: nl802154: make ieee802154_policy constant Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller --- net/ieee802154/nl_policy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index c7d71d1..83cb4cc 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -24,7 +24,7 @@ #define NLA_HW_ADDR NLA_U64 -struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { +const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, }, [IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, }, @@ -50,3 +50,4 @@ struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_DURATION] = { .type = NLA_U8, }, [IEEE802154_ATTR_ED_LIST] = { .len = 27 }, }; + -- cgit v1.1 From 8e753dd0a82bd266256c20a20b98dfa48f98d21e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Aug 2009 02:58:40 +0000 Subject: nl802154: add support for dumping WPAN interface information Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller --- net/ieee802154/netlink.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index a615b9d..3fe02b3 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -19,6 +19,7 @@ * Written by: * Sergey Lapin * Dmitry Eremin-Solenikov + * Maxim Osipov */ #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +75,7 @@ static int ieee802154_nl_finish(struct sk_buff *msg) /* XXX: nlh is right at the start of msg */ void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); - if (!genlmsg_end(msg, hdr)) + if (genlmsg_end(msg, hdr) < 0) goto out; return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, @@ -260,6 +262,35 @@ nla_put_failure: } EXPORT_SYMBOL(ieee802154_nl_scan_confirm); +static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, + u32 seq, int flags, struct net_device *dev) +{ + void *hdr; + + pr_debug("%s\n", __func__); + + hdr = genlmsg_put(msg, 0, seq, &ieee802154_coordinator_family, flags, + IEEE802154_LIST_IFACE); + if (!hdr) + goto out; + + NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); + NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); + + NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, + dev->dev_addr); + NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, + ieee802154_mlme_ops(dev)->get_short_addr(dev)); + NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID, + ieee802154_mlme_ops(dev)->get_pan_id(dev)); + return genlmsg_end(msg, hdr); + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + /* Requests from userspace */ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) { @@ -272,7 +303,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) dev = dev_get_by_name(&init_net, name); } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) dev = dev_get_by_index(&init_net, - nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); + nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); else return NULL; @@ -466,6 +497,67 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) return ret; } +static int ieee802154_list_iface(struct sk_buff *skb, + struct genl_info *info) +{ + /* Request for interface name, index, type, IEEE address, + PAN Id, short address */ + struct sk_buff *msg; + struct net_device *dev = NULL; + int rc = -ENOBUFS; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + goto out_dev; + + rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, + 0, dev); + if (rc < 0) + goto out_free; + + dev_put(dev); + + return genlmsg_unicast(&init_net, msg, info->snd_pid); +out_free: + nlmsg_free(msg); +out_dev: + dev_put(dev); + return rc; + +} + +static int ieee802154_dump_iface(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + int idx; + int s_idx = cb->args[0]; + + pr_debug("%s\n", __func__); + + idx = 0; + for_each_netdev(net, dev) { + if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) + goto cont; + + if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) + break; +cont: + idx++; + } + cb->args[0] = idx; + + return skb->len; +} + #define IEEE802154_OP(_cmd, _func) \ { \ .cmd = _cmd, \ @@ -475,12 +567,22 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) .flags = GENL_ADMIN_PERM, \ } +#define IEEE802154_DUMP(_cmd, _func, _dump) \ + { \ + .cmd = _cmd, \ + .policy = ieee802154_policy, \ + .doit = _func, \ + .dumpit = _dump, \ + } + static struct genl_ops ieee802154_coordinator_ops[] = { IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), + IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, + ieee802154_dump_iface), }; static int __init ieee802154_nl_init(void) -- cgit v1.1 From 74eda55db4335383ccdd1fb51a7b014202f89366 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Aug 2009 02:58:42 +0000 Subject: af_ieee802154: minor cleanup in dgram_bind 1) fix ro->bound protection by socket lock 2) make ro->bound bit instead of int Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller --- net/ieee802154/dgram.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index d1da6c6..25018a9 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -40,9 +40,10 @@ static DEFINE_RWLOCK(dgram_lock); struct dgram_sock { struct sock sk; - int bound; struct ieee802154_addr src_addr; struct ieee802154_addr dst_addr; + + unsigned bound:1; }; static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -86,18 +87,18 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) { struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; struct dgram_sock *ro = dgram_sk(sk); - int err = 0; + int err = -EINVAL; struct net_device *dev; + lock_sock(sk); + ro->bound = 0; if (len < sizeof(*addr)) - return -EINVAL; + goto out; if (addr->family != AF_IEEE802154) - return -EINVAL; - - lock_sock(sk); + goto out; dev = ieee802154_get_dev(sock_net(sk), &addr->addr); if (!dev) { @@ -113,6 +114,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr)); ro->bound = 1; + err = 0; out_put: dev_put(dev); out: -- cgit v1.1 From 99eb8558642b988055d2b8b16a334475550f78d3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Aug 2009 02:58:43 +0000 Subject: af_ieee802154: add support for WANT_ACK socket option Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller --- net/ieee802154/dgram.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 25018a9..77ae685 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -44,6 +44,7 @@ struct dgram_sock { struct ieee802154_addr dst_addr; unsigned bound:1; + unsigned want_ack:1; }; static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -51,7 +52,6 @@ static inline struct dgram_sock *dgram_sk(const struct sock *sk) return container_of(sk, struct dgram_sock, sk); } - static void dgram_hash(struct sock *sk) { write_lock_bh(&dgram_lock); @@ -74,6 +74,7 @@ static int dgram_init(struct sock *sk) ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; ro->dst_addr.pan_id = 0xffff; + ro->want_ack = 1; memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); return 0; } @@ -237,7 +238,10 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, skb_reset_network_header(skb); - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA | MAC_CB_FLAG_ACKREQ; + mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; + if (ro->want_ack) + mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, ro->bound ? &ro->src_addr : NULL, size); @@ -382,13 +386,59 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) static int dgram_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { - return -EOPNOTSUPP; + struct dgram_sock *ro = dgram_sk(sk); + + int val, len; + + if (level != SOL_IEEE802154) + return -EOPNOTSUPP; + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(unsigned int, len, sizeof(int)); + + switch (optname) { + case WPAN_WANTACK: + val = ro->want_ack; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; } static int dgram_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user optlen) { - return -EOPNOTSUPP; + struct dgram_sock *ro = dgram_sk(sk); + int val; + int err = 0; + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int __user *)optval)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case WPAN_WANTACK: + ro->want_ack = !!val; + break; + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; } struct proto ieee802154_dgram_prot = { -- cgit v1.1 From acb8aacda3f0bc3aeb652f4365c078a2b0adb0bf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Aug 2009 02:58:44 +0000 Subject: nl802154: support START-CONFIRM primitive Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: David S. Miller --- net/ieee802154/netlink.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'net') diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 3fe02b3..cd0567f 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -31,6 +31,7 @@ #include #include #include +#include #include static unsigned int ieee802154_seq_num; @@ -262,6 +263,31 @@ nla_put_failure: } EXPORT_SYMBOL(ieee802154_nl_scan_confirm); +int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) +{ + struct sk_buff *msg; + + pr_debug("%s\n", __func__); + + msg = ieee802154_nl_create(0, IEEE802154_START_CONF); + if (!msg) + return -ENOBUFS; + + NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); + NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); + NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, + dev->dev_addr); + + NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); + + return ieee802154_nl_finish(msg); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} +EXPORT_SYMBOL(ieee802154_nl_start_confirm); + static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev) { @@ -462,6 +488,12 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); + if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { + ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); + dev_put(dev); + return -EINVAL; + } + ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, bcn_ord, sf_ord, pan_coord, blx, coord_realign); -- cgit v1.1 From cb7d9e7f9bedee2928fe5ce95e1d5f1f22c1f2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 11 Aug 2009 03:12:07 +0000 Subject: Phonet: fix /proc/net/phonet with network namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit seq_open_net() and seq_release() are needed for seq_file_net(). Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/socket.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/phonet/socket.c b/net/phonet/socket.c index aa1617a..5f26c37 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -498,7 +498,8 @@ static const struct seq_operations pn_sock_seq_ops = { static int pn_sock_open(struct inode *inode, struct file *file) { - return seq_open(file, &pn_sock_seq_ops); + return seq_open_net(inode, file, &pn_sock_seq_ops, + sizeof(struct seq_net_private)); } const struct file_operations pn_sock_seq_fops = { @@ -506,5 +507,5 @@ const struct file_operations pn_sock_seq_fops = { .open = pn_sock_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_net, }; -- cgit v1.1 From a6fa32866567351503db8a5c3466a676ba08595f Mon Sep 17 00:00:00 2001 From: Jens Rosenboom Date: Wed, 12 Aug 2009 22:16:04 +0000 Subject: ipv6: Log the explicit address that triggered DAD failure If an interface has multiple addresses, the current message for DAD failure isn't really helpful, so this patch adds the address itself to the printk. Signed-off-by: Jens Rosenboom Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 9eb68e9..1ba42bd 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -955,8 +955,8 @@ static void ndisc_recv_na(struct sk_buff *skb) */ if (skb->pkt_type != PACKET_LOOPBACK) ND_PRINTK1(KERN_WARNING - "ICMPv6 NA: someone advertises our address on %s!\n", - ifp->idev->dev->name); + "ICMPv6 NA: someone advertises our address %pI6 on %s!\n", + &ifp->addr, ifp->idev->dev->name); in6_ifa_put(ifp); return; } -- cgit v1.1 From 3982d3d28b02bed5312015762fe2112ccbaf9051 Mon Sep 17 00:00:00 2001 From: "Fischer, Anna" Date: Thu, 13 Aug 2009 06:55:16 +0000 Subject: net/bridge: Add 'hairpin' port forwarding mode This patch adds a 'hairpin' (also called 'reflective relay') mode port configuration to the Linux Ethernet bridge kernel module. A bridge supporting hairpin forwarding mode can send frames back out through the port the frame was received on. Hairpin mode is required to support basic VEPA (Virtual Ethernet Port Aggregator) capabilities. You can find additional information on VEPA here: http://tech.groups.yahoo.com/group/evb/ http://www.ieee802.org/1/files/public/docs2009/new-hudson-vepa_seminar-20090514d.pdf http://www.internet2.edu/presentations/jt2009jul/20090719-congdon.pdf An additional patch 'bridge-utils: Add 'hairpin' port forwarding mode' is provided to allow configuring hairpin mode from userspace tools. Signed-off-by: Paul Congdon Signed-off-by: Anna Fischer Acked-by: Arnd Bergmann Signed-off-by: David S. Miller --- net/bridge/br_forward.c | 3 ++- net/bridge/br_if.c | 1 + net/bridge/br_private.h | 3 +++ net/bridge/br_sysfs_if.c | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index d2c27c8..bc1704a 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -22,7 +22,8 @@ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) { - return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING); + return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && + p->state == BR_STATE_FORWARDING); } static inline unsigned packet_length(const struct sk_buff *skb) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index eb404dc..e486f1f 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -256,6 +256,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; + p->flags = 0; br_init_port(p); p->state = BR_STATE_DISABLED; br_stp_port_timer_init(p); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d5b5537..8319247 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -81,6 +81,9 @@ struct net_bridge_port struct timer_list message_age_timer; struct kobject kobj; struct rcu_head rcu; + + unsigned long flags; +#define BR_HAIRPIN_MODE 0x00000001 }; struct net_bridge diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 4a3cdf8..820643a 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -143,6 +143,22 @@ static ssize_t store_flush(struct net_bridge_port *p, unsigned long v) } static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); +static ssize_t show_hairpin_mode(struct net_bridge_port *p, char *buf) +{ + int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0; + return sprintf(buf, "%d\n", hairpin_mode); +} +static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v) +{ + if (v) + p->flags |= BR_HAIRPIN_MODE; + else + p->flags &= ~BR_HAIRPIN_MODE; + return 0; +} +static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR, + show_hairpin_mode, store_hairpin_mode); + static struct brport_attribute *brport_attrs[] = { &brport_attr_path_cost, &brport_attr_priority, @@ -159,6 +175,7 @@ static struct brport_attribute *brport_attrs[] = { &brport_attr_forward_delay_timer, &brport_attr_hold_timer, &brport_attr_flush, + &brport_attr_hairpin_mode, NULL }; -- cgit v1.1 From e9b3cc1b3779fe10a80de4c3e7404bd308d0eae3 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 13 Aug 2009 05:19:44 +0000 Subject: net: skb ftracer - add tracepoint to skb_copy_datagram_iovec (v3) skb allocation / cosumption tracer - Add consumption tracepoint This patch adds a tracepoint to skb_copy_datagram_iovec, which is called each time a userspace process copies a frame from a socket receive queue to a user space buffer. It allows us to hook in and examine each sk_buff that the system receives on a per-socket bases, and can be use to compile a list of which skb's were received by which processes. Signed-off-by: Neil Horman include/trace/events/skb.h | 20 ++++++++++++++++++++ net/core/datagram.c | 3 +++ 2 files changed, 23 insertions(+) Signed-off-by: David S. Miller --- net/core/datagram.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/core/datagram.c b/net/core/datagram.c index b0fe692..1c6cf3a 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -55,6 +55,7 @@ #include #include #include +#include /* * Is a socket 'connection oriented' ? @@ -284,6 +285,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, int i, copy = start - offset; struct sk_buff *frag_iter; + trace_skb_copy_datagram_iovec(skb, len); + /* Copy header. */ if (copy > 0) { if (copy > len) -- cgit v1.1 From e651f03afe833326faa0abe55948c1c6cfd0b8ac Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 9 Aug 2009 08:12:48 +0000 Subject: inet6: Conversion from u8 to int This replaces assignments of the type "int on LHS" = "u8 on RHS" with simpler code. The LHS can express all of the unsigned right hand side values, hence the assigned value can not be negative. Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- net/ipv6/icmp.c | 17 ++++------------- net/ipv6/ip6_output.c | 15 +++++---------- net/ipv6/ipv6_sockglue.c | 2 -- net/ipv6/raw.c | 5 +---- net/ipv6/udp.c | 5 +---- 5 files changed, 11 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index eab62a7..e2325f6 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -323,7 +323,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, int iif = 0; int addr_type = 0; int len; - int hlimit, tclass; + int hlimit; int err = 0; if ((u8 *)hdr < skb->head || @@ -469,10 +469,6 @@ route_done: if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); - tclass = np->tclass; - if (tclass < 0) - tclass = 0; - msg.skb = skb; msg.offset = skb_network_offset(skb); msg.type = type; @@ -488,8 +484,8 @@ route_done: err = ip6_append_data(sk, icmpv6_getfrag, &msg, len + sizeof(struct icmp6hdr), - sizeof(struct icmp6hdr), - hlimit, tclass, NULL, &fl, (struct rt6_info*)dst, + sizeof(struct icmp6hdr), hlimit, + np->tclass, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT); if (err) { ip6_flush_pending_frames(sk); @@ -522,7 +518,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct dst_entry *dst; int err = 0; int hlimit; - int tclass; saddr = &ipv6_hdr(skb)->daddr; @@ -562,10 +557,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb) if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); - tclass = np->tclass; - if (tclass < 0) - tclass = 0; - idev = in6_dev_get(skb->dev); msg.skb = skb; @@ -573,7 +564,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) msg.type = ICMPV6_ECHO_REPLY; err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), - sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl, + sizeof(struct icmp6hdr), hlimit, np->tclass, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT); if (err) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 93beee9..6ad5aad 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -194,7 +194,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, struct ipv6hdr *hdr; u8 proto = fl->proto; int seg_len = skb->len; - int hlimit, tclass; + int hlimit = -1; + int tclass = 0; u32 mtu; if (opt) { @@ -237,19 +238,13 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, /* * Fill in the IPv6 header */ - - hlimit = -1; - if (np) + if (np) { + tclass = np->tclass; hlimit = np->hop_limit; + } if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); - tclass = -1; - if (np) - tclass = np->tclass; - if (tclass < 0) - tclass = 0; - *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel; hdr->payload_len = htons(seg_len); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index a7fdf9a..c390b1e 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1037,8 +1037,6 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, case IPV6_TCLASS: val = np->tclass; - if (val < 0) - val = 0; break; case IPV6_RECVTCLASS: diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d6c3c1c..5068410 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -877,11 +877,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, hlimit = ip6_dst_hoplimit(dst); } - if (tclass < 0) { + if (tclass < 0) tclass = np->tclass; - if (tclass < 0) - tclass = 0; - } if (msg->msg_flags&MSG_CONFIRM) goto do_confirm; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index d79fa67..20d2ffc 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -946,11 +946,8 @@ do_udp_sendmsg: hlimit = ip6_dst_hoplimit(dst); } - if (tclass < 0) { + if (tclass < 0) tclass = np->tclass; - if (tclass < 0) - tclass = 0; - } if (msg->msg_flags&MSG_CONFIRM) goto do_confirm; -- cgit v1.1 From 26ced1e4aa181c01379b0b7ef156a29c000d1f8c Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 9 Aug 2009 08:12:49 +0000 Subject: inet6: Set default traffic class This patch addresses: * assigning -1 to np->tclass as it is currently done is not very meaningful, since it turns into 0xff; * RFC 3542, 6.5 allows -1 for clearing the sticky IPV6_TCLASS option and specifies -1 to mean "use kernel default": - RFC 2460, 7. requires that the default traffic class must be zero for all 8 bits, - this is consistent with RFC 2474, 4.1 which recommends a default PHB of 0, in combination with a value of the ECN field of "non-ECT" (RFC 3168, 5.). This patch changes the meaning of -1 from assigning 255 to mean the RFC 2460 default, which at the same time allows to satisfy clearing the sticky TCLASS option as per RFC 3542, 6.5. (When passing -1 as ancillary data, the fallback remains np->tclass, which has either been set via socket options, or contains the default value.) Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- net/ipv6/ipv6_sockglue.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index c390b1e..f5e0682 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -315,6 +315,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, goto e_inval; if (val < -1 || val > 0xff) goto e_inval; + /* RFC 3542, 6.5: default traffic class of 0x0 */ + if (val == -1) + val = 0; np->tclass = val; retv = 0; break; -- cgit v1.1 From c03e20fc9a6ec5741d9df561130ecba38ef50eb6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 4 Aug 2009 15:06:26 -0700 Subject: mac80211: fix compilation of mesh (although its disabled) Mesh is currently disabled on mac80211, its marked as broken. This patch gets it to compile though, to account for the mac80211 workqueue changes. There was a simple typo in the patches for mesh for the workqueue migration, but we never compile tested it as we couldn't even select mesh as its broken. Lets at least let it compile for those interested in getting it fixed. Reported-by: Pat Erley Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 10 +++++----- net/mac80211/mesh_hwmp.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2f4f518..8c068e2 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -54,7 +54,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) return; } - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); } /** @@ -357,7 +357,7 @@ static void ieee80211_mesh_path_timer(unsigned long data) return; } - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); } struct mesh_table *mesh_table_grow(struct mesh_table *tbl) @@ -471,7 +471,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; ifmsh->housekeeping = true; - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED); } @@ -619,7 +619,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) if (ieee80211_vif_is_mesh(&sdata->vif)) - ieee80211_queue_work(local->hw.workqueue, &sdata->u.mesh.work); + ieee80211_queue_work(&local->hw, &sdata->u.mesh.work); rcu_read_unlock(); } @@ -692,7 +692,7 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: skb_queue_tail(&ifmsh->skb_queue, skb); - ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&local->hw, &ifmsh->work); return RX_QUEUED; } diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 11ab71a..e1a763e 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -660,14 +660,14 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) spin_unlock(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) - ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&sdata->local->hw, &ifmsh->work); else if (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around */ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1; - ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(&sdata->local->hw, &ifmsh->work); } else mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq + min_preq_int_jiff(sdata)); -- cgit v1.1 From a42dd7efd934888833c01199dbd21b242100ee92 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Wed, 5 Aug 2009 15:07:13 +0800 Subject: wireless: display wext SSID when connected by cfg80211 cfg80211 displays correct link info when connected by wext. But if the connection is setup by cfg80211, wext cannot display the SSID. This patch fixed this issue. Cc: Johannes Berg Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/wireless/wext-sme.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 7bacbd1..e4a054a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -206,7 +206,15 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->flags = 0; wdev_lock(wdev); - if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + if (wdev->current_bss) { + const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, + WLAN_EID_SSID); + if (ie) { + data->flags = 1; + data->length = ie[1]; + memcpy(ssid, ie + 2, data->length); + } + } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { data->flags = 1; data->length = wdev->wext.connect.ssid_len; memcpy(ssid, wdev->wext.connect.ssid, data->length); -- cgit v1.1 From b935df01ed4f0848f29b1e39c4f95d87b0206dea Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Thu, 6 Aug 2009 04:52:42 -0400 Subject: cfg80211: fix disassociation warning due to misuse of wdev->current_bss WARN_ON was triggered at mlme.c:213 when dissociating from an AP. wdev->current_bss->pub.bssid should be used in place of wdev->current_bss for BSSID comparison. Signed-off-by: Pavel Roskin Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 525e8e2..b44b6c0 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -198,7 +198,7 @@ static void __cfg80211_send_disassoc(struct net_device *dev, return; if (wdev->current_bss && - memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { for (i = 0; i < MAX_AUTH_BSSES; i++) { if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) continue; -- cgit v1.1 From e21546a2a3953a7d4b9d5c9b1cf12184ffceab96 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Aug 2009 20:41:32 +0200 Subject: mac80211: stay authenticated after disassoc After being disassociated by the AP, mac80211 currently reports this to cfg80211, and then goes to delete the association. That's fine, but cfg80211 assumes that it's still authenticated, however, mac80211 throws away all state. This fixes mac80211 to keep track of the authentication in that case so that cfg80211 can request a deauth or new association properly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 31 ++++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 630a438..9895917 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -284,6 +284,7 @@ struct ieee80211_if_managed { struct mutex mtx; struct ieee80211_bss *associated; + struct ieee80211_mgd_work *old_associate_work; struct list_head work_list; u8 bssid[ETH_ALEN]; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d5a1ee..c374d2d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -880,10 +880,11 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, - struct ieee80211_bss *bss, + struct ieee80211_mgd_work *wk, u32 bss_info_changed) { struct ieee80211_local *local = sdata->local; + struct ieee80211_bss *bss = wk->bss; bss_info_changed |= BSS_CHANGED_ASSOC; /* set timing information */ @@ -896,6 +897,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); sdata->u.mgd.associated = bss; + sdata->u.mgd.old_associate_work = wk; memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN); /* just to be sure */ @@ -1010,7 +1012,8 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; } -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) +static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, + bool deauth) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -1028,6 +1031,16 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) ifmgd->associated = NULL; memset(ifmgd->bssid, 0, ETH_ALEN); + if (deauth) { + kfree(ifmgd->old_associate_work); + ifmgd->old_associate_work = NULL; + } else { + struct ieee80211_mgd_work *wk = ifmgd->old_associate_work; + + wk->state = IEEE80211_MGD_STATE_IDLE; + list_add(&wk->list, &ifmgd->work_list); + } + /* * we need to commit the associated = NULL change because the * scan code uses that to determine whether this iface should @@ -1345,7 +1358,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, sdata->dev->name, bssid, reason_code); if (!wk) { - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); } else { list_del(&wk->list); kfree(wk); @@ -1378,7 +1391,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", sdata->dev->name, reason_code); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, false); return RX_MGMT_CFG80211_DISASSOC; } @@ -1581,7 +1594,8 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; bss_conf->assoc_capability = capab_info; - ieee80211_set_associated(sdata, wk->bss, changed); + /* this will take ownership of wk */ + ieee80211_set_associated(sdata, wk, changed); /* * Start timer to probe the connection to the AP now. @@ -1590,7 +1604,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); mod_beacon_timer(sdata); - kfree(wk); return RX_MGMT_CFG80211_ASSOC; } @@ -2096,7 +2109,7 @@ static void ieee80211_sta_work(struct work_struct *work) printk(KERN_DEBUG "No probe response from AP %pM" " after %dms, disconnecting.\n", bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); mutex_unlock(&ifmgd->mtx); /* * must be outside lock due to cfg80211, @@ -2500,7 +2513,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { bssid = req->bss->bssid; - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); } else list_for_each_entry(wk, &ifmgd->work_list, list) { if (&wk->bss->cbss == req->bss) { bssid = req->bss->bssid; @@ -2552,7 +2565,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return -ENOLINK; } - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, false); mutex_unlock(&ifmgd->mtx); -- cgit v1.1 From e458b8a22d33eef62765c9f89889efeb4041f073 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Aug 2009 20:41:33 +0200 Subject: cfg80211: fix nl80211 disconnected events When reporting a disconnection to userspace, we try to report whether it was from the AP or by our own choice. However, we misreported a broadcast deauth or disassoc as being by own choice, which is wrong. Fix this by checking the sender address instead of the destination address. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index b44b6c0..51d5df6 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -149,7 +149,7 @@ static void __cfg80211_send_deauth(struct net_device *dev, reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, @@ -215,7 +215,7 @@ static void __cfg80211_send_disassoc(struct net_device *dev, reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } -- cgit v1.1 From b6f0b639089fb160b10984ac56e07e7043dabad7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Aug 2009 20:41:34 +0200 Subject: cfg80211: fix SME association after disassociation When an AP disassociates us, we currently go into a weird state because the SME doesn't handle authenticated but not associated well unless it's within its own state machine, it can't recover from that. However, it shouldn't need to, since we don't do any decisions in it really -- so when we get disconnected, simply deauthenticate too. Reported-by: Pavel Roskin Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8a7dcbf..0b776b7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -570,10 +570,30 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->ssid_len = 0; if (wdev->conn) { + const u8 *bssid; + int ret; + kfree(wdev->conn->ie); wdev->conn->ie = NULL; kfree(wdev->conn); wdev->conn = NULL; + + /* + * If this disconnect was due to a disassoc, we + * we might still have an auth BSS around. For + * the userspace SME that's currently expected, + * but for the kernel SME (nl80211 CONNECT or + * wireless extensions) we want to clear up all + * state. + */ + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i]) + continue; + bssid = wdev->auth_bsses[i]->pub.bssid; + ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, + WLAN_REASON_DEAUTH_LEAVING); + WARN(ret, "deauth failed: %d\n", ret); + } } nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); -- cgit v1.1 From bcba8eae12fce23686b84a56d19f5fc78bf182ae Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 6 Aug 2009 21:04:41 +0200 Subject: cfg80211: Set WEP ciphers With iwconfig there is no way to properly set the ciphers when trying to connect to a WEP SSID. Although mac80211 based drivers dont need it, several fullmac drivers do. This patch basically sets the WEP ciphers whenever they're not set at all. Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- net/wireless/sme.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 0b776b7..340934f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -658,14 +658,28 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (connkeys && connkeys->def >= 0) { int idx; + u32 cipher; idx = connkeys->def; + cipher = connkeys->params[idx].cipher; /* If given a WEP key we may need it for shared key auth */ - if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || - connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { + if (cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104) { connect->key_idx = idx; connect->key = connkeys->params[idx].key; connect->key_len = connkeys->params[idx].key_len; + + /* + * If ciphers are not set (e.g. when going through + * iwconfig), we have to set them appropriately here. + */ + if (connect->crypto.cipher_group == 0) + connect->crypto.cipher_group = cipher; + + if (connect->crypto.n_ciphers_pairwise == 0) { + connect->crypto.n_ciphers_pairwise = 1; + connect->crypto.ciphers_pairwise[0] = cipher; + } } } -- cgit v1.1 From 59bbb6f7574bc693ed8313b98eac641116c95b94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 17:22:35 +0200 Subject: cfg80211: validate channel settings across interfaces Currently, there's a problem that affects regulatory enforcement and connection stability, in that it is possible to switch the channel while connected to a network or joined to an IBSS. The problem comes from the fact that we only validate the channel against the current interface's type, not against any other interface. Thus, you have any type of interface up, additionally bring up a monitor mode interface and switch the channel on the monitor. This will obviously also switch the channel on the other interface. The problem now is that if you do that while sending beacons for IBSS mode, you can switch to a disabled channel or a channel that doesn't allow beaconing. Combined with a managed mode interface connected to an AP instead of an IBSS interface, you can easily break the connection that way. To fix this, this patch validates any channel change with all available interfaces, and disallows such changes on secondary interfaces if another interface is connected to an AP or joined to an IBSS. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/Makefile | 3 +- net/wireless/chan.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 6 ++++ net/wireless/ibss.c | 61 ++++++++++++++++++++------------ net/wireless/nl80211.c | 54 +++++++--------------------- net/wireless/sme.c | 9 +++++ net/wireless/wext-compat.c | 55 ++++++++++------------------- net/wireless/wext-compat.h | 3 +- net/wireless/wext-sme.c | 65 +++++++++++++++++++--------------- 9 files changed, 213 insertions(+), 131 deletions(-) create mode 100644 net/wireless/chan.c (limited to 'net') diff --git a/net/wireless/Makefile b/net/wireless/Makefile index d74cc77..3ecaa91 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,8 @@ 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 mlme.o ibss.o sme.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o +cfg80211-y += mlme.o ibss.o sme.o chan.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o diff --git a/net/wireless/chan.c b/net/wireless/chan.c new file mode 100644 index 0000000..bc00c9a --- /dev/null +++ b/net/wireless/chan.c @@ -0,0 +1,88 @@ +/* + * This file contains helper code to handle channel + * settings and keeping track of what is possible at + * any point in time. + * + * Copyright 2009 Johannes Berg + */ + +#include +#include "core.h" + +struct ieee80211_channel * +rdev_fixed_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev) +{ + struct wireless_dev *wdev; + struct ieee80211_channel *result = NULL; + + WARN_ON(!mutex_is_locked(&rdev->devlist_mtx)); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (wdev == for_wdev) + continue; + + /* + * Lock manually to tell lockdep about allowed + * nesting here if for_wdev->mtx is held already. + * This is ok as it's all under the rdev devlist + * mutex and as such can only be done once at any + * given time. + */ + mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING); + if (wdev->current_bss) + result = wdev->current_bss->pub.channel; + wdev_unlock(wdev); + + if (result) + break; + } + + return result; +} + +int rdev_set_freq(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *chan; + struct ieee80211_sta_ht_cap *ht_cap; + int result; + + if (rdev_fixed_channel(rdev, NULL)) + return -EBUSY; + + if (!rdev->ops->set_channel) + return -EOPNOTSUPP; + + chan = ieee80211_get_channel(&rdev->wiphy, freq); + + /* Primary channel not allowed */ + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + if (channel_type == NL80211_CHAN_HT40MINUS && + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return -EINVAL; + else if (channel_type == NL80211_CHAN_HT40PLUS && + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return -EINVAL; + + ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; + + if (channel_type != NL80211_CHAN_NO_HT) { + if (!ht_cap->ht_supported) + return -EINVAL; + + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + return -EINVAL; + } + + result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type); + if (result) + return result; + + rdev->channel = chan; + + return 0; +} diff --git a/net/wireless/core.h b/net/wireless/core.h index 325c17e..5696b95 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -366,4 +366,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); +struct ieee80211_channel * +rdev_fixed_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev); +int rdev_set_freq(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type channel_type); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 4d7a084..42840a0 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -78,10 +78,15 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan; int err; ASSERT_WDEV_LOCK(wdev); + chan = rdev_fixed_channel(rdev, wdev); + if (chan && chan != params->channel) + return -EBUSY; + if (wdev->ssid_len) return -EALREADY; @@ -112,9 +117,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -264,27 +271,32 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, int cfg80211_ibss_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan; - int err; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan = NULL; + int err, freq; /* call only for ibss! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (chan && IS_ERR(chan)) - return PTR_ERR(chan); + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; - if (chan && - (chan->flags & IEEE80211_CHAN_NO_IBSS || - chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_NO_IBSS || + chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } if (wdev->wext.ibss.channel == chan) return 0; @@ -292,8 +304,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -307,9 +318,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -347,6 +360,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; @@ -354,14 +368,13 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -375,9 +388,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -414,6 +429,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; @@ -421,7 +437,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) + if (!rdev->ops->join_ibss) return -EOPNOTSUPP; if (ap_addr->sa_family != ARPHRD_ETHER) @@ -443,8 +459,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, wdev_lock(wdev); err = 0; if (wdev->ssid_len) - err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); + err = __cfg80211_leave_ibss(rdev, dev, true); wdev_unlock(wdev); if (err) @@ -456,9 +471,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); - err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0cd5482..2ff7376 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -701,15 +701,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; u32 freq; - if (!rdev->ops->set_channel) { - result = -EOPNOTSUPP; - goto bad_res; - } - result = -EINVAL; if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { @@ -723,42 +716,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = ieee80211_get_channel(&rdev->wiphy, freq); - - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - goto bad_res; - - if (channel_type == NL80211_CHAN_HT40MINUS && - (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)) - goto bad_res; - else if (channel_type == NL80211_CHAN_HT40PLUS && - (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)) - goto bad_res; - - /* - * At this point we know if that if HT40 was requested - * we are allowed to use it and the extension channel - * exists. - */ - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - /* no HT capabilities or intolerant */ - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - goto bad_res; - if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - goto bad_res; - } - - result = rdev->ops->set_channel(&rdev->wiphy, chan, - channel_type); + mutex_lock(&rdev->devlist_mtx); + result = rdev_set_freq(rdev, freq, channel_type); + mutex_unlock(&rdev->devlist_mtx); if (result) goto bad_res; - - rdev->channel = chan; } changed = 0; @@ -3453,7 +3416,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_crypto_settings crypto; - struct ieee80211_channel *chan; + struct ieee80211_channel *chan, *fixedchan; const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; int err, ssid_len, ie_len = 0; bool use_mfp = false; @@ -3496,6 +3459,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } + mutex_lock(&rdev->devlist_mtx); + fixedchan = rdev_fixed_channel(rdev, NULL); + if (fixedchan && chan != fixedchan) { + err = -EBUSY; + mutex_unlock(&rdev->devlist_mtx); + goto out; + } + mutex_unlock(&rdev->devlist_mtx); + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 340934f..219c3bc 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -256,9 +256,11 @@ void cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; + mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); wdev_lock(wdev); __cfg80211_sme_scan_done(dev); wdev_unlock(wdev); + mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); } void cfg80211_sme_rx_auth(struct net_device *dev, @@ -644,6 +646,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan; int err; ASSERT_WDEV_LOCK(wdev); @@ -651,6 +654,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; + chan = rdev_fixed_channel(rdev, wdev); + if (chan && chan != connect->channel) + return -EBUSY; + if (WARN_ON(wdev->connect_keys)) { kfree(wdev->connect_keys); wdev->connect_keys = NULL; @@ -785,9 +792,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, { int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_connect(rdev, dev, connect, connkeys); wdev_unlock(dev->ieee80211_ptr); + mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e4e90e2..17648dc 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -267,39 +267,26 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); * @wiphy: the wiphy * @freq: the wext freq encoding * - * Returns a channel, %NULL for auto, or an ERR_PTR for errors! + * Returns a frequency, or a negative error code, or 0 for auto. */ -struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, - struct iw_freq *freq) +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) { - struct ieee80211_channel *chan; - int f; - /* - * Parse frequency - return NULL for auto and + * Parse frequency - return 0 for auto and * -EINVAL for impossible things. */ if (freq->e == 0) { if (freq->m < 0) - return NULL; - f = ieee80211_channel_to_frequency(freq->m); + return 0; + return ieee80211_channel_to_frequency(freq->m); } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) div /= 10; if (div <= 0) - return ERR_PTR(-EINVAL); - f = freq->m / div; + return -EINVAL; + return freq->m / div; } - - /* - * Look up channel struct and return -EINVAL when - * it cannot be found. - */ - chan = ieee80211_get_channel(wiphy, f); - if (!chan) - return ERR_PTR(-EINVAL); - return chan; } int cfg80211_wext_siwrts(struct net_device *dev, @@ -761,33 +748,29 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode); int cfg80211_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - int err; + int freq, err; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); + return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); + return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); default: - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (!chan) + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + if (freq == 0) return -EINVAL; - if (IS_ERR(chan)) - return PTR_ERR(chan); - err = rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); - if (err) - return err; - rdev->channel = chan; - return 0; + mutex_lock(&rdev->devlist_mtx); + err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + mutex_unlock(&rdev->devlist_mtx); + return err; } } -EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 9a37747..20b3dae 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -42,8 +42,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid); -struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, - struct iw_freq *freq); +int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); extern const struct iw_handler_def cfg80211_wext_handler; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index e4a054a..fe1a536 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -52,25 +52,31 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, int cfg80211_mgd_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, - struct iw_freq *freq, char *extra) + struct iw_freq *wextfreq, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - int err; + struct ieee80211_channel *chan = NULL; + int err, freq; /* call only for station! */ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return -EINVAL; - chan = cfg80211_wext_freq(wdev->wiphy, freq); - if (chan && IS_ERR(chan)) - return PTR_ERR(chan); + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; - if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; + if (freq) { + chan = ieee80211_get_channel(wdev->wiphy, freq); + if (!chan) + return -EINVAL; + if (chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + } cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -84,9 +90,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -95,17 +100,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, wdev->wext.connect.channel = chan; /* SSID is not set, we just want to switch channel */ - if (wdev->wext.connect.ssid_len && chan) { - err = -EOPNOTSUPP; - if (rdev->ops->set_channel) - err = rdev->ops->set_channel(wdev->wiphy, chan, - NL80211_CHAN_NO_HT); + if (chan && !wdev->wext.connect.ssid_len) { + err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); goto out; } - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); return err; } @@ -143,6 +146,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); size_t len = data->length; int err; @@ -157,7 +161,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; - cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = 0; @@ -173,9 +178,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - event); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -186,10 +190,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, wdev->wext.connect.crypto.control_port = false; - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); return err; } @@ -230,6 +235,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, struct sockaddr *ap_addr, char *extra) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); u8 *bssid = ap_addr->sa_data; int err; @@ -244,7 +250,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; - cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -258,9 +265,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) goto out; - err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), - dev, WLAN_REASON_DEAUTH_LEAVING, - false); + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } @@ -271,10 +277,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, } else wdev->wext.connect.bssid = NULL; - err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); return err; } -- cgit v1.1 From f401a6f7ede753e56b84025e7d2db0d5ef560ce6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 14:51:05 +0200 Subject: cfg80211: use reassociation when possible With the move of everything related to the SME from mac80211 to cfg80211, we lost the ability to send reassociation frames. This adds them back, but only for wireless extensions. With the userspace SME, it shall control assoc vs. reassoc (it already can do so with the nl80211 interface). I haven't touched the connect() implementation, so it is not possible to reassociate with the nl80211 connect primitive. I think that should be done with the NL80211_CMD_ROAM command, but we'll have to see how that can be handled in the future, especially with fullmac chips. This patch addresses only the immediate regression we had in mac80211, which previously sent reassoc. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.h | 4 +++- net/wireless/mlme.c | 11 ++++++++++ net/wireless/sme.c | 54 ++++++++++++++++++++++++++++++++++++++----------- net/wireless/wext-sme.c | 8 +++++++- 4 files changed, 63 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/wireless/core.h b/net/wireless/core.h index 5696b95..92e0492 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -335,7 +335,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, @@ -353,6 +354,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); /* internal helpers */ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 51d5df6..da64071 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -67,6 +67,16 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + /* + * This is a bit of a hack, we don't notify userspace of + * a (re-)association reply if we tried to send a reassoc + * and got a reject -- we only try again with an assoc + * frame instead of reassoc. + */ + if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && + cfg80211_sme_failed_reassoc(wdev)) + goto out; + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); if (status_code == WLAN_STATUS_SUCCESS) { @@ -97,6 +107,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) cfg80211_put_bss(&bss->pub); } + out: wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 219c3bc..104b33e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -27,10 +27,10 @@ struct cfg80211_conn { CFG80211_CONN_ASSOCIATE_NEXT, CFG80211_CONN_ASSOCIATING, } state; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 *ie; size_t ie_len; - bool auto_auth; + bool auto_auth, prev_bssid_valid; }; @@ -110,6 +110,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; + const u8 *prev_bssid = NULL; int err; ASSERT_WDEV_LOCK(wdev); @@ -135,15 +136,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; - /* - * We could, later, implement roaming here and then actually - * set prev_bssid to non-NULL. But then we need to be aware - * that some APs don't like that -- so we'd need to retry - * the association. - */ + if (wdev->conn->prev_bssid_valid) + prev_bssid = wdev->conn->prev_bssid; err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, - NULL, + prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); @@ -316,6 +313,28 @@ void cfg80211_sme_rx_auth(struct net_device *dev, } } +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!wdev->conn)) + return false; + + if (!wdev->conn->prev_bssid_valid) + return false; + + /* + * Some stupid APs don't accept reassoc, so we + * need to fall back to trying regular assoc. + */ + wdev->conn->prev_bssid_valid = false; + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + + return true; +} + void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, @@ -359,8 +378,11 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) + if (bssid && status == WLAN_STATUS_SUCCESS) { memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif @@ -511,6 +533,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif } @@ -643,7 +667,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_channel *chan; @@ -742,6 +767,11 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; + if (prev_bssid) { + memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); + wdev->conn->prev_bssid_valid = true; + } + /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; @@ -794,7 +824,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys); + err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1a536..9074700 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -15,6 +15,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_cached_keys *ck = NULL; + const u8 *prev_bssid = NULL; int err, i; ASSERT_RDEV_LOCK(rdev); @@ -42,8 +43,12 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, for (i = 0; i < 6; i++) ck->params[i].key = ck->data[i]; } + + if (wdev->wext.prev_bssid_valid) + prev_bssid = wdev->wext.prev_bssid; + err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck); + &wdev->wext.connect, ck, prev_bssid); if (err) kfree(ck); @@ -184,6 +189,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, goto out; } + wdev->wext.prev_bssid_valid = false; wdev->wext.connect.ssid = wdev->wext.ssid; memcpy(wdev->wext.ssid, ssid, len); wdev->wext.connect.ssid_len = len; -- cgit v1.1 From f5ea9120be2e5d5c846243416cfdce01d02f5836 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 16:17:38 +0200 Subject: nl80211: add generation number to all dumps In order for userspace to be able to figure out whether it obtained a consistent snapshot of data or not when using netlink dumps, we need to have a generation number in each dump message that indicates whether the list has changed or not -- its value is arbitrary. This patch adds such a number to all dumps, this needs some mac80211 involvement to keep track of a generation number to start with when adding/removing mesh paths or stations. The wiphy and netdev lists can be fully handled within cfg80211, of course, but generation numbers need to be stored there as well. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 4 ++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/mesh.h | 2 ++ net/mac80211/mesh_pathtbl.c | 5 +++++ net/mac80211/sta_info.c | 2 ++ net/wireless/core.c | 5 +++++ net/wireless/core.h | 2 ++ net/wireless/nl80211.c | 31 +++++++++++++++++++++---------- net/wireless/scan.c | 1 + 9 files changed, 43 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4bbf500..5608f6c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -323,6 +323,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + sinfo->generation = sdata->local->sta_generation; + sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | @@ -909,6 +911,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, else memset(next_hop, 0, ETH_ALEN); + pinfo->generation = mesh_paths_generation; + pinfo->filled = MPATH_INFO_FRAME_QLEN | MPATH_INFO_DSN | MPATH_INFO_METRIC | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9895917..9943322 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -678,6 +678,7 @@ struct ieee80211_local { struct list_head sta_list; struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; + int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 2a2ed18..ce53881 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -265,6 +265,8 @@ void mesh_path_discard_frame(struct sk_buff *skb, void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); +extern int mesh_paths_generation; + #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 04b9e4d..431865a 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -38,6 +38,8 @@ struct mpath_node { static struct mesh_table *mesh_paths; static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ +int mesh_paths_generation; + /* This lock will have the grow table function as writer and add / delete nodes * as readers. When reading the table (i.e. doing lookups) we are well protected * by RCU @@ -243,6 +245,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) grow = 1; + mesh_paths_generation++; + spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { @@ -484,6 +488,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: + mesh_paths_generation++; spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); return err; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a360bce..eec0014 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -349,6 +349,7 @@ int sta_info_insert(struct sta_info *sta) goto out_free; } list_add(&sta->list, &local->sta_list); + local->sta_generation++; local->num_sta++; sta_info_hash_add(local, sta); @@ -485,6 +486,7 @@ static void __sta_info_unlink(struct sta_info **sta) } local->num_sta--; + local->sta_generation++; if (local->ops->sta_notify) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) diff --git a/net/wireless/core.c b/net/wireless/core.c index 1e18930..62e1ac0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -32,6 +32,7 @@ MODULE_DESCRIPTION("wireless configuration support"); * only read the list, and that can happen quite * often because we need to do it for each command */ LIST_HEAD(cfg80211_rdev_list); +int cfg80211_rdev_list_generation; /* * This is used to protect the cfg80211_rdev_list @@ -511,6 +512,7 @@ int wiphy_register(struct wiphy *wiphy) wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); list_add(&rdev->list, &cfg80211_rdev_list); + cfg80211_rdev_list_generation++; mutex_unlock(&cfg80211_mutex); @@ -593,6 +595,7 @@ void wiphy_unregister(struct wiphy *wiphy) reg_device_remove(wiphy); list_del(&rdev->list); + cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); debugfs_remove(rdev->wiphy.debugfsdir); @@ -653,6 +656,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); + rdev->devlist_generation++; /* can only change netns with wiphy */ dev->features |= NETIF_F_NETNS_LOCAL; @@ -733,6 +737,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); + rdev->devlist_generation++; mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.keys); diff --git a/net/wireless/core.h b/net/wireless/core.h index 92e0492..639db52 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -49,6 +49,7 @@ struct cfg80211_registered_device { /* associate netdev list */ struct mutex devlist_mtx; struct list_head netdev_list; + int devlist_generation; /* BSSes/scanning */ spinlock_t bss_lock; @@ -101,6 +102,7 @@ bool wiphy_idx_valid(int wiphy_idx) extern struct mutex cfg80211_mutex; extern struct list_head cfg80211_rdev_list; +extern int cfg80211_rdev_list_generation; #define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex)) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2ff7376..b3d5c1d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -408,6 +408,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + cfg80211_rdev_list_generation); + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, dev->wiphy.retry_short); NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, @@ -825,6 +828,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); + + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, + rdev->devlist_generation ^ + (cfg80211_rdev_list_generation << 2)); + return genlmsg_end(msg, hdr); nla_put_failure: @@ -838,12 +846,12 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * int if_idx = 0; int wp_start = cb->args[0]; int if_start = cb->args[1]; - struct cfg80211_registered_device *dev; + struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; if (wp_idx < wp_start) { wp_idx++; @@ -851,21 +859,21 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if_idx = 0; - mutex_lock(&dev->devlist_mtx); - list_for_each_entry(wdev, &dev->netdev_list, list) { + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev, &rdev->netdev_list, list) { if (if_idx < if_start) { if_idx++; continue; } if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - dev, wdev->netdev) < 0) { - mutex_unlock(&dev->devlist_mtx); + rdev, wdev->netdev) < 0) { + mutex_unlock(&rdev->devlist_mtx); goto out; } if_idx++; } - mutex_unlock(&dev->devlist_mtx); + mutex_unlock(&rdev->devlist_mtx); wp_idx++; } @@ -1616,6 +1624,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation); + sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; @@ -2101,6 +2111,8 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation); + pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); if (!pinfoattr) goto nla_put_failure; @@ -3090,8 +3102,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (!hdr) return -1; - NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, - rdev->bss_generation); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); bss = nla_nest_start(msg, NL80211_ATTR_BSS); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0ccf3a0..1bcb131 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -562,6 +562,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) spin_lock_bh(&dev->bss_lock); list_del(&bss->list); + dev->bss_generation++; rb_erase(&bss->rbn, &dev->bss_tree); spin_unlock_bh(&dev->bss_lock); -- cgit v1.1 From ad5351db89681515681c5d5659ddf4c69e3cc6f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 16:42:15 +0200 Subject: mac80211: allow DMA optimisation If we have a lot of frames to transmit at once, for instance with fragmentation, it can be an optimisation to only tell the DMA engine about them on the last fragment/frame to avoid banging the IO too much. This patch allows implementation such an optimisation by telling the driver when more frames can be expected. Currently, this is used by mac80211 only on fragmented frames, but could also be used in the future on other frames when the queue was full and there are multiple frames pending. Note that drivers need to be careful when using this flag, they need to kick their DMA engines not just when this flag is clear, but also when the queue gets full so that progress can be made. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7cffaa0..7f2e4cd 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1154,6 +1154,9 @@ static int __ieee80211_tx(struct ieee80211_local *local, next = skb->next; len = skb->len; + if (next) + info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + sdata = vif_to_sdata(info->control.vif); switch (sdata->vif.type) { -- cgit v1.1 From 5ba63533bbf653631faab60f6988506160ec6ba4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Aug 2009 17:54:07 +0200 Subject: cfg80211: fix alignment problem in scan request The memory layout for scan requests was rather wrong, we put the scan SSIDs before the channels which could lead to the channel pointers being unaligned in memory. It turns out that using a pointer to the channel array isn't necessary anyway since we can embed a zero-length array into the struct. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/main.c | 16 ++++++++-------- net/mac80211/scan.c | 10 +++++----- net/wireless/nl80211.c | 3 +-- net/wireless/scan.c | 4 ++-- net/wireless/sme.c | 3 +-- 6 files changed, 18 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9943322..d6bd7dd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -715,7 +715,7 @@ struct ieee80211_local { struct mutex scan_mtx; unsigned long scanning; struct cfg80211_ssid scan_ssid; - struct cfg80211_scan_request int_scan_req; + struct cfg80211_scan_request *int_scan_req; struct cfg80211_scan_request *scan_req; struct ieee80211_channel *scan_channel; const u8 *orig_ies; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0c4f8e1..b03fd84 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -765,9 +765,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_ht = supp_ht || sband->ht_cap.ht_supported; } - local->int_scan_req.n_channels = channels; - local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL); - if (!local->int_scan_req.channels) + local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + + sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req) return -ENOMEM; /* if low-level driver supports AP, we also support VLAN */ @@ -882,13 +882,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* alloc internal scan request */ i = 0; - local->int_scan_req.ssids = &local->scan_ssid; - local->int_scan_req.n_ssids = 1; + 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] = + local->int_scan_req->channels[i] = &hw->wiphy->bands[band]->channels[j]; i++; } @@ -920,7 +920,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: - kfree(local->int_scan_req.channels); + kfree(local->int_scan_req->channels); return result; } EXPORT_SYMBOL(ieee80211_register_hw); @@ -962,7 +962,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) wiphy_unregister(local->hw.wiphy); ieee80211_wep_free(local); ieee80211_led_exit(local); - kfree(local->int_scan_req.channels); + kfree(local->int_scan_req); } EXPORT_SYMBOL(ieee80211_unregister_hw); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 244f53f..e091cbc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -277,7 +277,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (test_bit(SCAN_HW_SCANNING, &local->scanning)) ieee80211_restore_scan_ies(local); - if (local->scan_req != &local->int_scan_req) + if (local->scan_req != local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; @@ -423,7 +423,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->scan_req = req; local->scan_sdata = sdata; - if (req != &local->int_scan_req && + if (req != local->int_scan_req && sdata->vif.type == NL80211_IFTYPE_STATION && !list_empty(&ifmgd->work_list)) { /* actually wait for the work it's doing to finish/time out */ @@ -743,10 +743,10 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, if (local->scan_req) goto unlock; - memcpy(local->int_scan_req.ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); - local->int_scan_req.ssids[0].ssid_len = ssid_len; + memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req->ssids[0].ssid_len = ssid_len; - ret = __ieee80211_start_scan(sdata, &sdata->local->int_scan_req); + ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); unlock: mutex_unlock(&local->scan_mtx); return ret; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b3d5c1d..667a87d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3002,10 +3002,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - request->channels = (void *)((char *)request + sizeof(*request)); request->n_channels = n_channels; if (n_ssids) - request->ssids = (void *)(request->channels + n_channels); + request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = n_ssids; if (ie_len) { if (request->ssids) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1bcb131..e6c1f11 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -612,8 +612,8 @@ int cfg80211_wext_siwscan(struct net_device *dev, creq->wiphy = wiphy; creq->dev = dev; - creq->ssids = (void *)(creq + 1); - creq->channels = (void *)(creq->ssids + 1); + /* SSIDs come after channels */ + creq->ssids = (void *)&creq->channels[n_channels]; creq->n_channels = n_channels; creq->n_ssids = 1; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 104b33e..8e2ef54 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -65,7 +65,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!request) return -ENOMEM; - request->channels = (void *)((char *)request + sizeof(*request)); if (wdev->conn->params.channel) request->channels[0] = wdev->conn->params.channel; else { @@ -82,7 +81,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) } } request->n_channels = n_channels; - request->ssids = (void *)(request->channels + n_channels); + request->ssids = (void *)&request->channels[n_channels]; request->n_ssids = 1; memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, -- cgit v1.1 From 4b181144e6c1c25aaba9b9fc7cc70c95495ecb92 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 8 Aug 2009 11:03:58 +0200 Subject: cfg80211: fix locking for SIWFREQ "cfg80211: validate channel settings across interfaces" contained a locking bug -- in the managed-mode SIWFREQ call it would end up running into a lock recursion. This fixes it by not checking that particular interface for a channel that it needs to stay on, which is as it should be as that's the interface we're setting the channel for. Reported-by: Reinette Chatre Reported-by: Kalle Valo Signed-off-by: Johannes Berg Tested-by: Kalle Valo Tested-by: Reinette Chatre Signed-off-by: John W. Linville --- net/wireless/chan.c | 3 ++- net/wireless/core.h | 1 + net/wireless/nl80211.c | 2 +- net/wireless/wext-compat.c | 2 +- net/wireless/wext-sme.c | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index bc00c9a..a46ac6c 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -42,13 +42,14 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev, } int rdev_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type) { struct ieee80211_channel *chan; struct ieee80211_sta_ht_cap *ht_cap; int result; - if (rdev_fixed_channel(rdev, NULL)) + if (rdev_fixed_channel(rdev, for_wdev)) return -EBUSY; if (!rdev->ops->set_channel) diff --git a/net/wireless/core.h b/net/wireless/core.h index 639db52..f7be3a9 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -374,6 +374,7 @@ struct ieee80211_channel * rdev_fixed_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *for_wdev); int rdev_set_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 667a87d..a8aaade 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -721,7 +721,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); mutex_lock(&rdev->devlist_mtx); - result = rdev_set_freq(rdev, freq, channel_type); + result = rdev_set_freq(rdev, NULL, freq, channel_type); mutex_unlock(&rdev->devlist_mtx); if (result) goto bad_res; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 17648dc..c449174 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -766,7 +766,7 @@ int cfg80211_wext_siwfreq(struct net_device *dev, if (freq == 0) return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 9074700..d16cd9e 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -106,7 +106,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* SSID is not set, we just want to switch channel */ if (chan && !wdev->wext.connect.ssid_len) { - err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT); + err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); goto out; } -- cgit v1.1 From d5b96a6f39a8aaa7534069b3db71048df44f023b Mon Sep 17 00:00:00 2001 From: Pat Erley Date: Sat, 8 Aug 2009 17:53:19 -0400 Subject: mac80211: remove max_bandwidth This removes the max_bandwidth attribute. It is only ever written to, and is duplicated by max_bandwidth_khz in the regulatory code. Signed-off-by: Pat Erley Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/reg.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0f61ae6..fc7a484 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1018,7 +1018,6 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); chan->max_power = chan->orig_mpwr = (int) MBM_TO_DBM(power_rule->max_eirp); return; @@ -1027,7 +1026,6 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, (int) MBI_TO_DBI(power_rule->max_antenna_gain)); - chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); if (chan->orig_mpwr) chan->max_power = min(chan->orig_mpwr, (int) MBM_TO_DBM(power_rule->max_eirp)); @@ -1329,7 +1327,6 @@ static void handle_channel_custom(struct wiphy *wiphy, chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz); chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } -- cgit v1.1 From aee83eaff859694642b323553f93b9eb59141144 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 9 Aug 2009 11:51:29 +0200 Subject: cfg80211: add missing device list locking When calling into the wext code from the NETDEV_UP notifier, we need to hold the devlist_mtx mutex as the wext code ends up calling into channel checks. Reported-by: Kalle Valo Signed-off-by: Johannes Berg Tested-by: Kalle Valo Signed-off-by: John W. Linville --- net/wireless/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 62e1ac0..e630648 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -710,6 +710,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: @@ -722,6 +723,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); #endif break; -- cgit v1.1 From feff1f2fea3fc7ee055e46d0fbdb2d25ad2b14c4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Aug 2009 16:01:54 +0200 Subject: mac80211: take statistics before encryption When encryption is used, the number of bytes sent to the peer increases by the IV and ICV. This is accounted if software encryption is used, but not if the devices does hardware encryption. To make the numbers comparable, never account for that overhead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7f2e4cd..588c18a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -844,6 +844,23 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) } static ieee80211_tx_result debug_noinline +ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) +{ + struct sk_buff *skb = tx->skb; + + if (!tx->sta) + return TX_CONTINUE; + + tx->sta->tx_packets++; + do { + tx->sta->tx_fragments++; + tx->sta->tx_bytes += skb->len; + } while ((skb = skb->next)); + + return TX_CONTINUE; +} + +static ieee80211_tx_result debug_noinline ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) { if (!tx->key) @@ -887,23 +904,6 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) return TX_CONTINUE; } -static ieee80211_tx_result debug_noinline -ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) -{ - struct sk_buff *skb = tx->skb; - - if (!tx->sta) - return TX_CONTINUE; - - tx->sta->tx_packets++; - do { - tx->sta->tx_fragments++; - tx->sta->tx_bytes += skb->len; - } while ((skb = skb->next)); - - return TX_CONTINUE; -} - /* actual transmit path */ /* @@ -1213,9 +1213,9 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) CALL_TXH(ieee80211_tx_h_sequence) CALL_TXH(ieee80211_tx_h_fragment) /* handlers after fragment must be aware of tx info fragmentation! */ + CALL_TXH(ieee80211_tx_h_stats) CALL_TXH(ieee80211_tx_h_encrypt) CALL_TXH(ieee80211_tx_h_calculate_duration) - CALL_TXH(ieee80211_tx_h_stats) #undef CALL_TXH txh_done: -- cgit v1.1 From d0b69609dc50b6bf2637cf74e6f97c251adc65ed Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Aug 2009 16:02:40 +0200 Subject: mac80211: sequence number micro-optimisation There's no need to mask the variable with 0xFFF0 since we ever only use it as a u16 and the lowest four bits can't ever be non-zero. The compiler cannot infer the latter, and therefore has to emit code to do the masking. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 588c18a..0c24a4b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -700,7 +700,6 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) /* for pure STA mode without beacons, we can do it */ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number); tx->sdata->sequence_number += 0x10; - tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ; return TX_CONTINUE; } -- cgit v1.1 From 62b1208e146af048e90a32bc4fe5fba85e4fa51e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Aug 2009 16:04:15 +0200 Subject: mac80211: small tx code cleanup It's really easier to read if it's not indented as much, so invert the condition and rearrange the code so the smaller chunk is indented instead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0c24a4b..3244298 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -317,30 +317,30 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) if (!atomic_read(&tx->sdata->bss->num_sta_ps)) return TX_CONTINUE; + /* buffered in hardware */ + if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) { + info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; + + return TX_CONTINUE; + } + /* buffered in mac80211 */ - if (tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) { - if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) - purge_old_ps_buffers(tx->local); - if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= - AP_MAX_BC_BUFFER) { + if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) + purge_old_ps_buffers(tx->local); + + if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: BC TX buffer full - " - "dropping the oldest frame\n", - tx->dev->name); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: BC TX buffer full - dropping the oldest frame\n", + tx->dev->name); #endif - dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); - } else - tx->local->total_ps_buffered++; - skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); - return TX_QUEUED; - } + dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); + } else + tx->local->total_ps_buffered++; - /* buffered in hardware */ - info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; + skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); - return TX_CONTINUE; + return TX_QUEUED; } static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, -- cgit v1.1 From fe58343461def0d376908a80cebd087b746a1483 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:46 -0700 Subject: mac80211: Improve dequeing from mpath frame queue. Also, fix typo in comment. Signed-off-by: Javier Cardona Tested-by: Andrey Yurovsky Signed-off-by: John W. Linville --- net/mac80211/mesh.h | 2 +- net/mac80211/mesh_hwmp.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index ce53881..6aaf1ec 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -61,7 +61,7 @@ enum mesh_path_flags { * retry * @discovery_retries: number of discovery retries * @flags: mesh path flags, as specified on &enum mesh_path_flags - * @state_lock: mesh pat state lock + * @state_lock: mesh path state lock * * * The combination of dst and sdata is unique in the mesh path table. Since the diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index e1a763e..b54c21c 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -810,10 +810,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb, } if (skb_queue_len(&mpath->frame_queue) >= - MESH_FRAME_QUEUE_LEN) { - skb_to_free = mpath->frame_queue.next; - skb_unlink(skb_to_free, &mpath->frame_queue); - } + MESH_FRAME_QUEUE_LEN) + skb_to_free = skb_dequeue(&mpath->frame_queue); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; skb_queue_tail(&mpath->frame_queue, skb); -- cgit v1.1 From a9e3091bf08ddea35f172549a8a21d5bd6ee6129 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Mon, 10 Aug 2009 12:15:47 -0700 Subject: mac80211: Use correct sign for mesh active path refresh. On locally originated traffic, we refresh active paths after a timeout. The decision to do this was using the wrong sign and therefore the refresh timer was triggered for every frame. Signed-off-by: Andrey Yurovsky Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_hwmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index b54c21c..1cd1e727 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -791,7 +791,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, } if (mpath->flags & MESH_PATH_ACTIVE) { - if (time_after(jiffies, mpath->exp_time - + if (time_after(jiffies, mpath->exp_time + msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && !memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) -- cgit v1.1 From 3c5772a5279de9eadfff7adb5ddea08106495fff Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:48 -0700 Subject: mac80211: Use 3-address format for mesh broadcast frames. The 11s task group recently changed the frame mesh multicast/broadcast frame format to use 3-address. This was done to avoid interactions with widely deployed lazy-WDS access points. This patch changes the format of group addressed frames, both mesh-originated and proxied, to use the data format defined in draft D2.08 and forward. The address fields used for group addressed frames is: In 802.11 header ToDS:0 FromDS:1 addr1: DA (broadcast/multicast address) addr2: TA addr3: Mesh SA In address extension header: addr4: SA (only present if frame was proxied) Note that this change breaks backward compatibility with earlier mesh stack versions. Signed-off-by: Andrey Yurovsky Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++---- net/mac80211/mesh.h | 5 ++++- net/mac80211/rx.c | 45 +++++++++++++++++++++++-------------- net/mac80211/tx.c | 64 ++++++++++++++++++++++++++++------------------------- net/wireless/util.c | 16 ++++++++++---- 5 files changed, 136 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8c068e2..10d9338 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -399,21 +399,75 @@ endgrow: } /** + * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame + * @hdr: 802.11 frame header + * @fc: frame control field + * @meshda: destination address in the mesh + * @meshsa: source address address in the mesh. Same as TA, as frame is + * locally originated. + * + * Return the length of the 802.11 (does not include a mesh control header) + */ +int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char + *meshda, char *meshsa) { + if (is_multicast_ether_addr(meshda)) { + *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + /* DA TA SA */ + memcpy(hdr->addr1, meshda, ETH_ALEN); + memcpy(hdr->addr2, meshsa, ETH_ALEN); + memcpy(hdr->addr3, meshsa, ETH_ALEN); + return 24; + } else { + *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); + /* RA TA DA SA */ + memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ + memcpy(hdr->addr2, meshsa, ETH_ALEN); + memcpy(hdr->addr3, meshda, ETH_ALEN); + memcpy(hdr->addr4, meshsa, ETH_ALEN); + return 30; + } +} + +/** * ieee80211_new_mesh_header - create a new mesh header * @meshhdr: uninitialized mesh header * @sdata: mesh interface to be used + * @addr4: addr4 of the mesh frame (1st in ae header) + * may be NULL + * @addr5: addr5 of the mesh frame (1st or 2nd in ae header) + * may be NULL unless addr6 is present + * @addr6: addr6 of the mesh frame (2nd or 3rd in ae header) + * may be NULL unless addr5 is present * * Return the header length. */ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata) + struct ieee80211_sub_if_data *sdata, char *addr4, + char *addr5, char *addr6) { - meshhdr->flags = 0; + int aelen = 0; + memset(meshhdr, 0, sizeof(meshhdr)); meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); sdata->u.mesh.mesh_seqnum++; - - return 6; + if (addr4) { + meshhdr->flags |= MESH_FLAGS_AE_A4; + aelen += ETH_ALEN; + memcpy(meshhdr->eaddr1, addr4, ETH_ALEN); + } + if (addr5 && addr6) { + meshhdr->flags |= MESH_FLAGS_AE_A5_A6; + aelen += 2 * ETH_ALEN; + if (!addr4) { + memcpy(meshhdr->eaddr1, addr5, ETH_ALEN); + memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); + } else { + memcpy(meshhdr->eaddr2, addr5, ETH_ALEN); + memcpy(meshhdr->eaddr3, addr6, ETH_ALEN); + } + } + return 6 + aelen; } static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 6aaf1ec..2ebd74c 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -193,8 +193,11 @@ struct mesh_rmc { /* Public interfaces */ /* Various */ +int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, + char *da, char *sa); int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata); + struct ieee80211_sub_if_data *sdata, char *addr4, + char *addr5, char *addr6); int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, struct ieee80211_sub_if_data *sdata); bool mesh_matches_local(struct ieee802_11_elems *ie, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 25a669c..4cd9e45 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -489,12 +489,21 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + char *dev_addr = rx->dev->dev_addr; if (ieee80211_is_data(hdr->frame_control)) { - if (!ieee80211_has_a4(hdr->frame_control)) - return RX_DROP_MONITOR; - if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0) - return RX_DROP_MONITOR; + if (is_multicast_ether_addr(hdr->addr1)) { + if (ieee80211_has_tods(hdr->frame_control) || + !ieee80211_has_fromds(hdr->frame_control)) + return RX_DROP_MONITOR; + if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0) + return RX_DROP_MONITOR; + } else { + if (!ieee80211_has_a4(hdr->frame_control)) + return RX_DROP_MONITOR; + if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0) + return RX_DROP_MONITOR; + } } /* If there is not an established peer link and this is not a peer link @@ -527,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && - mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata)) + mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata)) return RX_DROP_MONITOR; #undef msh_h_get @@ -1495,7 +1504,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) /* illegal frame */ return RX_DROP_MONITOR; - if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){ + if (!is_multicast_ether_addr(hdr->addr1) && + (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) { struct mesh_path *mppath; rcu_read_lock(); @@ -1512,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) rcu_read_unlock(); } - if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) + /* Frame has reached destination. Don't forward */ + if (!is_multicast_ether_addr(hdr->addr1) && + compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) return RX_CONTINUE; mesh_hdr->ttl--; @@ -1532,22 +1544,21 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) rx->dev->name); fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; - /* - * Save TA to addr1 to send TA a path error if a - * suitable next hop is not found - */ - memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN); memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN); info = IEEE80211_SKB_CB(fwd_skb); memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; ieee80211_select_queue(local, fwd_skb); - if (is_multicast_ether_addr(fwd_hdr->addr3)) - memcpy(fwd_hdr->addr1, fwd_hdr->addr3, + if (!is_multicast_ether_addr(fwd_hdr->addr1)) { + int err; + /* + * Save TA to addr1 to send TA a path error if a + * suitable next hop is not found + */ + memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN); - else { - int err = mesh_nexthop_lookup(fwd_skb, sdata); + err = mesh_nexthop_lookup(fwd_skb, sdata); /* Failed to immediately resolve next hop: * fwded frame was dropped or will be added * later to the pending skb queue. */ @@ -1560,7 +1571,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } } - if (is_multicast_ether_addr(hdr->addr3) || + if (is_multicast_ether_addr(hdr->addr1) || rx->dev->flags & IFF_PROMISC) return RX_CONTINUE; else diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3244298..ee8aa76 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1414,9 +1414,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, if (ieee80211_vif_is_mesh(&sdata->vif) && ieee80211_is_data(hdr->frame_control)) { - if (is_multicast_ether_addr(hdr->addr3)) - memcpy(hdr->addr1, hdr->addr3, ETH_ALEN); - else + if (!is_multicast_ether_addr(hdr->addr1)) if (mesh_nexthop_lookup(skb, sdata)) { dev_put(sdata->dev); return; @@ -1619,52 +1617,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; ret = NETDEV_TX_OK; goto fail; } - memset(&mesh_hdr, 0, sizeof(mesh_hdr)); if (compare_ether_addr(dev->dev_addr, skb->data + ETH_ALEN) == 0) { - /* RA TA DA SA */ - memset(hdr.addr1, 0, ETH_ALEN); - memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); - meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); + hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, + skb->data, skb->data + ETH_ALEN); + meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, + sdata, NULL, NULL, NULL); } else { /* packet from other interface */ struct mesh_path *mppath; + int is_mesh_mcast = 1; + char *mesh_da; - memset(hdr.addr1, 0, ETH_ALEN); - memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN); - + rcu_read_lock(); if (is_multicast_ether_addr(skb->data)) - memcpy(hdr.addr3, skb->data, ETH_ALEN); + /* DA TA mSA AE:SA */ + mesh_da = skb->data; else { - rcu_read_lock(); mppath = mpp_path_lookup(skb->data, sdata); - if (mppath) - memcpy(hdr.addr3, mppath->mpp, ETH_ALEN); - else - memset(hdr.addr3, 0xff, ETH_ALEN); - rcu_read_unlock(); + if (mppath) { + /* RA TA mDA mSA AE:DA SA */ + mesh_da = mppath->mpp; + is_mesh_mcast = 0; + } else + /* DA TA mSA AE:SA */ + mesh_da = dev->broadcast; } + hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, + mesh_da, dev->dev_addr); + rcu_read_unlock(); + if (is_mesh_mcast) + meshhdrlen = + ieee80211_new_mesh_header(&mesh_hdr, + sdata, + skb->data + ETH_ALEN, + NULL, + NULL); + else + meshhdrlen = + ieee80211_new_mesh_header(&mesh_hdr, + sdata, + NULL, + skb->data, + skb->data + ETH_ALEN); - mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6; - mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; - put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum); - memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN); - memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN); - sdata->u.mesh.mesh_seqnum++; - meshhdrlen = 18; } - hdrlen = 30; break; #endif case NL80211_IFTYPE_STATION: diff --git a/net/wireless/util.c b/net/wireless/util.c index ba387d8..693275a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -274,11 +274,11 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) switch (ae) { case 0: return 6; - case 1: + case MESH_FLAGS_AE_A4: return 12; - case 2: + case MESH_FLAGS_AE_A5_A6: return 18; - case 3: + case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6): return 24; default: return 6; @@ -333,10 +333,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, } break; case cpu_to_le16(IEEE80211_FCTL_FROMDS): - if (iftype != NL80211_IFTYPE_STATION || + if ((iftype != NL80211_IFTYPE_STATION && + iftype != NL80211_IFTYPE_MESH_POINT) || (is_multicast_ether_addr(dst) && !compare_ether_addr(src, addr))) return -1; + if (iftype == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211s_hdr *meshdr = + (struct ieee80211s_hdr *) (skb->data + hdrlen); + hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + if (meshdr->flags & MESH_FLAGS_AE_A4) + memcpy(src, meshdr->eaddr1, ETH_ALEN); + } break; case cpu_to_le16(0): if (iftype != NL80211_IFTYPE_ADHOC) -- cgit v1.1 From d403a1c66ea1fb47ae61921eac278d4154f7ca8a Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:49 -0700 Subject: mac80211: Update the station failed frames average when minstrel is used. The fail_avg value is used to compute the mesh metric, and was only being set by the pid rate control module. This fixes the mesh path selection mechanism for cards that use mistrel for rate control. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 7c51429..3ea9740 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -155,12 +155,16 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, struct sk_buff *skb) { struct minstrel_sta_info *mi = priv_sta; + struct minstrel_priv *mp = (struct minstrel_priv *)priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; + struct ieee80211_local *local = hw_to_local(mp->hw); + struct sta_info *si; int i, ndx; int success; success = !!(info->flags & IEEE80211_TX_STAT_ACK); + si = sta_info_get(local, sta->addr); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (ar[i].idx < 0) @@ -172,8 +176,12 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, mi->r[ndx].attempts += ar[i].count; - if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) + if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) { mi->r[ndx].success += success; + if (si) + si->fail_avg = (18050 - mi->r[ndx].probability) + / 180; + } } if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) -- cgit v1.1 From 5815814bfad5b3f88ff58ab90aaf09dd69ab8da2 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:50 -0700 Subject: mac80211: Early detection of broken mesh paths when using minstrel. This change triggers a path discovery as soon as the link quality degrades below a certain threshold. This results in a faster path recovery time than by simply relying on the periodic path refresh mechanism to detect broken links. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 3ea9740..0071649 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -51,6 +51,7 @@ #include #include #include +#include "mesh.h" #include "rate.h" #include "rc80211_minstrel.h" @@ -178,9 +179,14 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) { mi->r[ndx].success += success; - if (si) + if (si) { si->fail_avg = (18050 - mi->r[ndx].probability) / 180; + WARN_ON(si->fail_avg > 100); + if (si->fail_avg == 100 && + ieee80211_vif_is_mesh(&si->sdata->vif)) + mesh_plink_broken(si); + } } } -- cgit v1.1 From 5b365834255d7c90fc724b032c814dfa297aacf9 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:51 -0700 Subject: mac80211: Assign a default mesh beaconing interval. The mesh stack was enabling beaconing without specifying an interval. This patch defines a default beaconing interval of 1s. Incidentally, this fixes mesh beaconing in mac80211_hwsim devices. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 6 ++++-- net/mac80211/mesh.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 10d9338..25d0065 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -525,9 +525,11 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; ifmsh->housekeeping = true; - ieee80211_queue_work(&local->hw, &ifmsh->work); + queue_work(local->hw, &ifmsh->work); + sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED); + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON_INT); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 2ebd74c..4241925 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -174,6 +174,7 @@ struct mesh_rmc { */ #define MESH_PATH_REFRESH_TIME 1000 #define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) +#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ #define MESH_MAX_PREQ_RETRIES 4 #define MESH_PATH_EXPIRE (600 * HZ) -- cgit v1.1 From 18889231e4527dfe23145efe318e74744794a95d Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 12:15:52 -0700 Subject: mac80211: Move mpath and mpp growth to mesh workqueue. This prevents calling rcu_synchronize from within the tx path by moving the table growth code to the mesh workqueue. Move mesh_table_free and mesh_table_grow from mesh.c to mesh_pathtbl.c and declare them static. Also, re-enable mesh in Kconfig and update the configuration description. Signed-off-by: Javier Cardona Tested-by: Andrey Yurovsky Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 8 +-- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mesh.c | 77 +++-------------------- net/mac80211/mesh.h | 20 +++++- net/mac80211/mesh_pathtbl.c | 146 +++++++++++++++++++++++++++++++++----------- 5 files changed, 144 insertions(+), 109 deletions(-) (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 7dd77b6..9db4ff8 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -66,12 +66,12 @@ endmenu config MAC80211_MESH bool "Enable mac80211 mesh networking (pre-802.11s) support" depends on MAC80211 && EXPERIMENTAL - depends on BROKEN ---help--- This options enables support of Draft 802.11s mesh networking. - The implementation is based on Draft 1.08 of the Mesh Networking - amendment. For more information visit http://o11s.org/. - + The implementation is based on Draft 2.08 of the Mesh Networking + amendment. However, no compliance with that draft is claimed or even + possible, as drafts leave a number of identifiers to be defined after + ratification. For more information visit http://o11s.org/. config MAC80211_LEDS bool "Enable LED triggers" diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d6bd7dd..a6abc7d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -355,7 +355,7 @@ struct ieee80211_if_mesh { unsigned long timers_running; - bool housekeeping; + unsigned long wrkq_flags; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; size_t mesh_id_len; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 25d0065..3185e18 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -47,7 +47,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - ifmsh->housekeeping = true; + ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING; if (local->quiescing) { set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); @@ -320,30 +320,6 @@ struct mesh_table *mesh_table_alloc(int size_order) return newtbl; } -static void __mesh_table_free(struct mesh_table *tbl) -{ - kfree(tbl->hash_buckets); - kfree(tbl->hashwlock); - kfree(tbl); -} - -void mesh_table_free(struct mesh_table *tbl, bool free_leafs) -{ - struct hlist_head *mesh_hash; - struct hlist_node *p, *q; - int i; - - mesh_hash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) { - spin_lock(&tbl->hashwlock[i]); - hlist_for_each_safe(p, q, &mesh_hash[i]) { - tbl->free_node(p, free_leafs); - atomic_dec(&tbl->entries); - } - spin_unlock(&tbl->hashwlock[i]); - } - __mesh_table_free(tbl); -} static void ieee80211_mesh_path_timer(unsigned long data) { @@ -360,44 +336,6 @@ static void ieee80211_mesh_path_timer(unsigned long data) ieee80211_queue_work(&local->hw, &ifmsh->work); } -struct mesh_table *mesh_table_grow(struct mesh_table *tbl) -{ - struct mesh_table *newtbl; - struct hlist_head *oldhash; - struct hlist_node *p, *q; - int i; - - if (atomic_read(&tbl->entries) - < tbl->mean_chain_len * (tbl->hash_mask + 1)) - goto endgrow; - - newtbl = mesh_table_alloc(tbl->size_order + 1); - if (!newtbl) - goto endgrow; - - newtbl->free_node = tbl->free_node; - newtbl->mean_chain_len = tbl->mean_chain_len; - newtbl->copy_node = tbl->copy_node; - atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); - - oldhash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) - hlist_for_each(p, &oldhash[i]) - if (tbl->copy_node(p, newtbl) < 0) - goto errcopy; - - return newtbl; - -errcopy: - for (i = 0; i <= newtbl->hash_mask; i++) { - hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) - tbl->free_node(p, 0); - } - __mesh_table_free(newtbl); -endgrow: - return NULL; -} - /** * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame * @hdr: 802.11 frame header @@ -487,7 +425,6 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, if (free_plinks != sdata->u.mesh.accepting_plinks) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); - ifmsh->housekeeping = false; mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); } @@ -524,8 +461,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; - ifmsh->housekeeping = true; - queue_work(local->hw, &ifmsh->work); + ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING; + ieee80211_queue_work(&local->hw, &ifmsh->work); sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | @@ -664,7 +601,13 @@ static void ieee80211_mesh_work(struct work_struct *work) ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) mesh_path_start_discovery(sdata); - if (ifmsh->housekeeping) + if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) + mesh_mpath_table_grow(); + + if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) + mesh_mpp_table_grow(); + + if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) ieee80211_mesh_housekeeping(sdata, ifmsh); } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 4241925..eb23fc6 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -44,6 +44,23 @@ enum mesh_path_flags { }; /** + * enum mesh_deferred_task_flags - mac80211 mesh deferred tasks + * + * + * + * @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks + * @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs + * to grow. + * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to + * grow + */ +enum mesh_deferred_task_flags { + MESH_WORK_HOUSEKEEPING, + MESH_WORK_GROW_MPATH_TABLE, + MESH_WORK_GROW_MPP_TABLE, +}; + +/** * struct mesh_path - mac80211 mesh path structure * * @dst: mesh path destination mac address @@ -250,7 +267,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, /* Mesh tables */ struct mesh_table *mesh_table_alloc(int size_order); void mesh_table_free(struct mesh_table *tbl, bool free_leafs); -struct mesh_table *mesh_table_grow(struct mesh_table *tbl); +void mesh_mpath_table_grow(void); +void mesh_mpp_table_grow(void); u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl); /* Mesh paths */ diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 431865a..751c4d0 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -39,6 +39,69 @@ static struct mesh_table *mesh_paths; static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; +static void __mesh_table_free(struct mesh_table *tbl) +{ + kfree(tbl->hash_buckets); + kfree(tbl->hashwlock); + kfree(tbl); +} + +void mesh_table_free(struct mesh_table *tbl, bool free_leafs) +{ + struct hlist_head *mesh_hash; + struct hlist_node *p, *q; + int i; + + mesh_hash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) { + spin_lock(&tbl->hashwlock[i]); + hlist_for_each_safe(p, q, &mesh_hash[i]) { + tbl->free_node(p, free_leafs); + atomic_dec(&tbl->entries); + } + spin_unlock(&tbl->hashwlock[i]); + } + __mesh_table_free(tbl); +} + +static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +{ + struct mesh_table *newtbl; + struct hlist_head *oldhash; + struct hlist_node *p, *q; + int i; + + if (atomic_read(&tbl->entries) + < tbl->mean_chain_len * (tbl->hash_mask + 1)) + goto endgrow; + + newtbl = mesh_table_alloc(tbl->size_order + 1); + if (!newtbl) + goto endgrow; + + newtbl->free_node = tbl->free_node; + newtbl->mean_chain_len = tbl->mean_chain_len; + newtbl->copy_node = tbl->copy_node; + atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); + + oldhash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) + hlist_for_each(p, &oldhash[i]) + if (tbl->copy_node(p, newtbl) < 0) + goto errcopy; + + return newtbl; + +errcopy: + for (i = 0; i <= newtbl->hash_mask; i++) { + hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) + tbl->free_node(p, 0); + } + __mesh_table_free(newtbl); +endgrow: + return NULL; +} + /* This lock will have the grow table function as writer and add / delete nodes * as readers. When reading the table (i.e. doing lookups) we are well protected @@ -187,6 +250,8 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data */ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -195,8 +260,6 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - might_sleep(); - if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -208,11 +271,11 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return -ENOSPC; err = -ENOMEM; - new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); + new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; - new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); + new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; @@ -250,20 +313,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) spin_unlock(&mesh_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { - struct mesh_table *oldtbl, *newtbl; - - write_lock(&pathtbl_resize_lock); - oldtbl = mesh_paths; - newtbl = mesh_table_grow(mesh_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return 0; - } - rcu_assign_pointer(mesh_paths, newtbl); - write_unlock(&pathtbl_resize_lock); - - synchronize_rcu(); - mesh_table_free(oldtbl, false); + set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); + ieee80211_queue_work(&local->hw, &ifmsh->work); } return 0; @@ -278,9 +329,46 @@ err_path_alloc: return err; } +void mesh_mpath_table_grow(void) +{ + struct mesh_table *oldtbl, *newtbl; + + write_lock(&pathtbl_resize_lock); + oldtbl = mesh_paths; + newtbl = mesh_table_grow(mesh_paths); + if (!newtbl) { + write_unlock(&pathtbl_resize_lock); + return; + } + rcu_assign_pointer(mesh_paths, newtbl); + write_unlock(&pathtbl_resize_lock); + + synchronize_rcu(); + mesh_table_free(oldtbl, false); +} + +void mesh_mpp_table_grow(void) +{ + struct mesh_table *oldtbl, *newtbl; + + write_lock(&pathtbl_resize_lock); + oldtbl = mpp_paths; + newtbl = mesh_table_grow(mpp_paths); + if (!newtbl) { + write_unlock(&pathtbl_resize_lock); + return; + } + rcu_assign_pointer(mpp_paths, newtbl); + write_unlock(&pathtbl_resize_lock); + + synchronize_rcu(); + mesh_table_free(oldtbl, false); +} int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -289,8 +377,6 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) int err = 0; u32 hash_idx; - might_sleep(); - if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) /* never add ourselves as neighbours */ return -ENOTSUPP; @@ -299,11 +385,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return -ENOTSUPP; err = -ENOMEM; - new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); + new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); if (!new_mpath) goto err_path_alloc; - new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); + new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); if (!new_node) goto err_node_alloc; @@ -337,20 +423,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) spin_unlock(&mpp_paths->hashwlock[hash_idx]); read_unlock(&pathtbl_resize_lock); if (grow) { - struct mesh_table *oldtbl, *newtbl; - - write_lock(&pathtbl_resize_lock); - oldtbl = mpp_paths; - newtbl = mesh_table_grow(mpp_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return 0; - } - rcu_assign_pointer(mpp_paths, newtbl); - write_unlock(&pathtbl_resize_lock); - - synchronize_rcu(); - mesh_table_free(oldtbl, false); + set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); + ieee80211_queue_work(&local->hw, &ifmsh->work); } return 0; -- cgit v1.1 From 1fe90b033e04d7402b3deb392420fe2ff57986a5 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 11 Aug 2009 11:18:42 -0300 Subject: trivial: remove duplicate "different" from comment Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: John W. Linville --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index fc7a484..f256dff 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1424,7 +1424,7 @@ static int ignore_request(struct wiphy *wiphy, if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different - * different Country IE alpha2s. We could + * Country IE alpha2s. We could * intersect them, but that seems unlikely * to be correct. Reject second one for now. */ -- cgit v1.1 From cca89496a8afe4ad12ce1e468e60cef1e27e4eee Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 10 Aug 2009 17:29:29 -0700 Subject: mac80211: Fix unresolved mesh frames queued without valid control.vif Mesh frames that could not be immediately resolved were queued with a NULL info->control.vif. This patch moves the call to mesh_nexthop_lookup closer to the point where it is handed over to ieee80211_tx(). This ensures that the unresolved frames are ready to be sent once the path is resolved. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/tx.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ee8aa76..0c08d1e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1412,14 +1412,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - if (ieee80211_vif_is_mesh(&sdata->vif) && - ieee80211_is_data(hdr->frame_control)) { - if (!is_multicast_ether_addr(hdr->addr1)) - if (mesh_nexthop_lookup(skb, sdata)) { - dev_put(sdata->dev); - return; - } - } else if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { + if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { int hdrlen; u16 len_rthdr; @@ -1476,6 +1469,15 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, info->control.vif = &sdata->vif; + if (ieee80211_vif_is_mesh(&sdata->vif) && + ieee80211_is_data(hdr->frame_control) && + !is_multicast_ether_addr(hdr->addr1)) + if (mesh_nexthop_lookup(skb, sdata)) { + /* skb queued: don't free */ + dev_put(sdata->dev); + return; + } + ieee80211_select_queue(local, skb); ieee80211_tx(sdata, skb, false); dev_put(sdata->dev); -- cgit v1.1 From 57ef5ddb456649667438e023f67dd3a3ff3fbed8 Mon Sep 17 00:00:00 2001 From: David Woo Date: Wed, 12 Aug 2009 11:03:43 -0700 Subject: mac80211: Mark a destination sequence number as valid when a PREQ is received. If a PREQ frame is received giving us a fresher DSN than what we have, record the new dsn and mark it as valid. This patch fixes a bug in the setting of the MESH_PATH_DSN_VALID flag. Also, minor fix to coding style on that file. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_hwmp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 1cd1e727..ef1efd3 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -397,7 +397,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - u8 *preq_elem, u32 metric) { + u8 *preq_elem, u32 metric) +{ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 *dst_addr, *orig_addr; @@ -430,7 +431,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, if ((!(mpath->flags & MESH_PATH_DSN_VALID)) || DSN_LT(mpath->dsn, dst_dsn)) { mpath->dsn = dst_dsn; - mpath->flags &= MESH_PATH_DSN_VALID; + mpath->flags |= MESH_PATH_DSN_VALID; } else if ((!(dst_flags & MP_F_DO)) && (mpath->flags & MESH_PATH_ACTIVE)) { reply = true; -- cgit v1.1 From 70bdb6b275d789ddf05c3a858e6b57715539394b Mon Sep 17 00:00:00 2001 From: David Woo Date: Wed, 12 Aug 2009 11:03:44 -0700 Subject: mac80211: Fix invalid length passed to IE parser for PLINK CONFIRM frames The length of the fixed portion of plink confirm frames is 4 bytes longer than the other plink_action frames. This path corrects an error in the length adjustment done for these type of frames. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_plink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cb14253..ffcbad7 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -409,7 +409,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { baseaddr += 4; - baselen -= 4; + baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peer_link) { -- cgit v1.1 From 36e6fea84905512ea776707e82b5b435220efc17 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Aug 2009 22:21:21 +0200 Subject: cfg80211: check for and abort dangling scan requests If you trigger a scan request on an interface and then take it down, or rmmod the module or unplug the device the driver might "forget" to cancel the scan request. That is a bug in the driver, but the current behaviour is that we just hang endlessly waiting for the netdev refcount to become 0 which it never will. To improve robustness, check for this situation in cfg80211, warn about it and clean up behind the driver. I don't just clean up silently because it's likely that the driver also has some internal state it has now leaked. Additionally, this fixes a locking bug, clearing the scan_req pointer should be done under the rdev lock. Finally, we also need to _wait_ for the scan work and not just abort it since it might be pending and wanting to do a cleanup. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 10 +++++++++- net/wireless/core.h | 1 + net/wireless/scan.c | 26 ++++++++++++++++---------- 3 files changed, 26 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index e630648..35d83be 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -601,8 +601,8 @@ void wiphy_unregister(struct wiphy *wiphy) mutex_unlock(&cfg80211_mutex); + flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); - cancel_work_sync(&rdev->scan_done_wk); kfree(rdev->scan_req); flush_work(&rdev->event_work); } @@ -728,6 +728,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif break; case NETDEV_UNREGISTER: + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER @@ -746,6 +753,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif } mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) diff --git a/net/wireless/core.h b/net/wireless/core.h index f7be3a9..c603f52 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -368,6 +368,7 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); struct ieee80211_channel * diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e6c1f11..fe575a2 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -18,19 +18,14 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) -void __cfg80211_scan_done(struct work_struct *wk) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) { - struct cfg80211_registered_device *rdev; struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif - rdev = container_of(wk, struct cfg80211_registered_device, - scan_done_wk); - - mutex_lock(&rdev->mtx); request = rdev->scan_req; dev = request->dev; @@ -43,9 +38,9 @@ void __cfg80211_scan_done(struct work_struct *wk) cfg80211_sme_scan_done(dev); if (request->aborted) - nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); + nl80211_send_scan_aborted(rdev, dev); else - nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); + nl80211_send_scan_done(rdev, dev); #ifdef CONFIG_WIRELESS_EXT if (!request->aborted) { @@ -57,11 +52,22 @@ void __cfg80211_scan_done(struct work_struct *wk) dev_put(dev); - cfg80211_unlock_rdev(rdev); - wiphy_to_dev(request->wiphy)->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } +void __cfg80211_scan_done(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + cfg80211_lock_rdev(rdev); + ___cfg80211_scan_done(rdev); + cfg80211_unlock_rdev(rdev); +} + void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); -- cgit v1.1 From 16cb9d42b68b339852e8914f2538ca9a2aec616c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Aug 2009 23:33:20 +0200 Subject: cfg80211: allow driver to override PS default Sometimes drivers might have a good reason to override the PS default, like iwlwifi right now where it affects RX performance significantly at this point. This will allow them to override the default, if desired, in a way that users can still change it according to their trade-off choices, not the driver's, like would happen if the driver just disabled PS completely then. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 35d83be..bc99e4e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -412,6 +412,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + rdev->wiphy.ps_default = CONFIG_CFG80211_DEFAULT_PS_VALUE; + wiphy_net_set(&rdev->wiphy, &init_net); rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; @@ -674,7 +676,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE; + wdev->wext.ps = wdev->wiphy->ps_default; wdev->wext.ps_timeout = 100; if (rdev->ops->set_power_mgmt) if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, -- cgit v1.1 From b85daa5324a3c9e560a4351c17d18f49c647eb5c Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Fri, 14 Aug 2009 12:41:07 +0000 Subject: vlan: adds fcoe offload related net_device_ops and updates fcoe_ddp_xid field Adds fcoe offload related net_device_ops functions vlan_dev_fcoe_ddp_setup and vlan_dev_fcoe_ddp_done, their implementation simply calls real eth device net_device_ops for FCoE DDP setup and done operations. Updates VLAN netdev field value for fcoe_ddp_xid from real eth device netdev. Above changes are required for fcoe DDP offload working on a VLAN interface. Signed-off-by: Vasu Dev Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- net/8021q/vlan.c | 3 +++ net/8021q/vlan_dev.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index a1f1630..e814794 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -391,6 +391,9 @@ static void vlan_transfer_features(struct net_device *dev, vlandev->features &= ~dev->vlan_features; vlandev->features |= dev->features & dev->vlan_features; vlandev->gso_max_size = dev->gso_max_size; +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; +#endif if (old_features != vlandev->features) netdev_features_change(vlandev); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 96bad8f..6e695ac 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -561,6 +561,33 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) return err; } +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, + struct scatterlist *sgl, unsigned int sgc) +{ + struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + const struct net_device_ops *ops = real_dev->netdev_ops; + int rc = 0; + + if (ops->ndo_fcoe_ddp_setup) + rc = ops->ndo_fcoe_ddp_setup(real_dev, xid, sgl, sgc); + + return rc; +} + +static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) +{ + struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + const struct net_device_ops *ops = real_dev->netdev_ops; + int len = 0; + + if (ops->ndo_fcoe_ddp_done) + len = ops->ndo_fcoe_ddp_done(real_dev, xid); + + return len; +} +#endif + static void vlan_dev_change_rx_flags(struct net_device *dev, int change) { struct net_device *real_dev = vlan_dev_info(dev)->real_dev; @@ -635,6 +662,10 @@ static int vlan_dev_init(struct net_device *dev) if (is_zero_ether_addr(dev->broadcast)) memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; +#endif + if (real_dev->features & NETIF_F_HW_VLAN_TX) { dev->header_ops = real_dev->header_ops; dev->hard_header_len = real_dev->hard_header_len; @@ -715,6 +746,10 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_change_rx_flags = vlan_dev_change_rx_flags, .ndo_do_ioctl = vlan_dev_ioctl, .ndo_neigh_setup = vlan_dev_neigh_setup, +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, + .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, +#endif }; static const struct net_device_ops vlan_netdev_accel_ops = { @@ -731,6 +766,10 @@ static const struct net_device_ops vlan_netdev_accel_ops = { .ndo_change_rx_flags = vlan_dev_change_rx_flags, .ndo_do_ioctl = vlan_dev_ioctl, .ndo_neigh_setup = vlan_dev_neigh_setup, +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, + .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, +#endif }; void vlan_setup(struct net_device *dev) -- cgit v1.1 From 1758c0947605211ef953cc91d6bbdf847a21b822 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 13 Aug 2009 22:54:25 +0000 Subject: can: Use WARN_ONCE() instead of BUG_ON() for sanity check in receive path To ensure a proper handling of CAN frames transported in skbuffs some checks need to be performed at receive time. As stated by Michael Olbrich and Luotao Fu BUG_ON() might be to restrictive. This is right as we can just drop the non conform skbuff and the Kernel can continue working. This patch replaces the BUG_ON() with a WARN_ONCE() so that the system remains healthy but we made the problem visible (once). Signed-off-by: Oliver Hartkopp Signed-off-by: Urs Thuermann CC: Michael Olbrich CC: Luotao Fu Signed-off-by: David S. Miller --- net/can/af_can.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/can/af_can.c b/net/can/af_can.c index e733725..f9c027b 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -651,12 +651,16 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, struct can_frame *cf = (struct can_frame *)skb->data; int matches; - if (dev->type != ARPHRD_CAN || !net_eq(dev_net(dev), &init_net)) { - kfree_skb(skb); - return 0; - } + if (!net_eq(dev_net(dev), &init_net)) + goto drop; - BUG_ON(skb->len != sizeof(struct can_frame) || cf->can_dlc > 8); + if (WARN_ONCE(dev->type != ARPHRD_CAN || + skb->len != sizeof(struct can_frame) || + cf->can_dlc > 8, + "PF_CAN: dropped non conform skbuf: " + "dev type %d, len %d, can_dlc %d\n", + dev->type, skb->len, cf->can_dlc)) + goto drop; /* update statistics */ can_stats.rx_frames++; @@ -683,6 +687,10 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, } return 0; + +drop: + kfree_skb(skb); + return 0; } /* -- cgit v1.1 From ae6e2aef6c03ccb06c6baaa1b77cc0e0ae226faf Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 17 Aug 2009 10:35:49 +0000 Subject: phonet: fix build when PROC_FS is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix phonet build when PROC_FS is not enabled: net/built-in.o: In function `pn_sock_open': socket.c:(.text+0x23c649): undefined reference to `seq_open_net' net/built-in.o:(.rodata+0x21018): undefined reference to `seq_release_net' Signed-off-by: Randy Dunlap Acked-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/socket.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 5f26c37..7a4ee39 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -413,6 +413,7 @@ found: } EXPORT_SYMBOL(pn_sock_get_port); +#ifdef CONFIG_PROC_FS static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos) { struct net *net = seq_file_net(seq); @@ -509,3 +510,4 @@ const struct file_operations pn_sock_seq_fops = { .llseek = seq_lseek, .release = seq_release_net, }; +#endif -- cgit v1.1 From 2bfb1070ba1fdb8cbc2b0b9ff61a3b0701ab40de Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 14 Aug 2009 16:13:12 +0400 Subject: ieee802154: add a sysfs representation of WPAN master devices Add a sysfs/in-kernel representation of LR-WPAN master devices. Signed-off-by: Dmitry Eremin-Solenikov --- net/ieee802154/Makefile | 2 +- net/ieee802154/wpan-class.c | 159 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 net/ieee802154/wpan-class.c (limited to 'net') diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index f99338a..4068a9f 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o +obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o wpan-class.o nl802154-y := netlink.o nl_policy.o af_802154-y := af_ieee802154.o raw.o dgram.o diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c new file mode 100644 index 0000000..f306604 --- /dev/null +++ b/net/ieee802154/wpan-class.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2007, 2008, 2009 Siemens AG + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include + +#define MASTER_SHOW_COMPLEX(name, format_string, args...) \ +static ssize_t name ## _show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \ + int ret; \ + \ + mutex_lock(&phy->pib_lock); \ + ret = sprintf(buf, format_string "\n", args); \ + mutex_unlock(&phy->pib_lock); \ + return ret; \ +} + +#define MASTER_SHOW(field, format_string) \ + MASTER_SHOW_COMPLEX(field, format_string, phy->field) + +MASTER_SHOW(current_channel, "%d"); +MASTER_SHOW(current_page, "%d"); +MASTER_SHOW(channels_supported, "%#x"); +MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB", + ((signed char) (phy->transmit_power << 2)) >> 2, + (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 ); +MASTER_SHOW(cca_mode, "%d"); + +static struct device_attribute pmib_attrs[] = { + __ATTR_RO(current_channel), + __ATTR_RO(current_page), + __ATTR_RO(channels_supported), + __ATTR_RO(transmit_power), + __ATTR_RO(cca_mode), + {}, +}; + +static void wpan_phy_release(struct device *d) +{ + struct wpan_phy *phy = container_of(d, struct wpan_phy, dev); + kfree(phy); +} + +static struct class wpan_phy_class = { + .name = "ieee802154", + .dev_release = wpan_phy_release, + .dev_attrs = pmib_attrs, +}; + +static DEFINE_MUTEX(wpan_phy_mutex); +static int wpan_phy_idx; + +static int wpan_phy_match(struct device *dev, void *data) +{ + return !strcmp(dev_name(dev), (const char *)data); +} + +struct wpan_phy *wpan_phy_find(const char *str) +{ + struct device *dev; + + if (WARN_ON(!str)) + return NULL; + + dev = class_find_device(&wpan_phy_class, NULL, + (void *)str, wpan_phy_match); + if (!dev) + return NULL; + + return container_of(dev, struct wpan_phy, dev); +} +EXPORT_SYMBOL(wpan_phy_find); + +static int wpan_phy_idx_valid(int idx) +{ + return idx >= 0; +} + +struct wpan_phy *wpan_phy_alloc(size_t priv_size) +{ + struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size, + GFP_KERNEL); + + mutex_lock(&wpan_phy_mutex); + phy->idx = wpan_phy_idx++; + if (unlikely(!wpan_phy_idx_valid(phy->idx))) { + wpan_phy_idx--; + mutex_unlock(&wpan_phy_mutex); + kfree(phy); + return NULL; + } + mutex_unlock(&wpan_phy_mutex); + + mutex_init(&phy->pib_lock); + + device_initialize(&phy->dev); + dev_set_name(&phy->dev, "wpan-phy%d", phy->idx); + + phy->dev.class = &wpan_phy_class; + + return phy; +} +EXPORT_SYMBOL(wpan_phy_alloc); + +int wpan_phy_register(struct device *parent, struct wpan_phy *phy) +{ + phy->dev.parent = parent; + + return device_add(&phy->dev); +} +EXPORT_SYMBOL(wpan_phy_register); + +void wpan_phy_unregister(struct wpan_phy *phy) +{ + device_del(&phy->dev); +} +EXPORT_SYMBOL(wpan_phy_unregister); + +void wpan_phy_free(struct wpan_phy *phy) +{ + put_device(&phy->dev); +} +EXPORT_SYMBOL(wpan_phy_free); + +static int __init wpan_phy_class_init(void) +{ + return class_register(&wpan_phy_class); +} +subsys_initcall(wpan_phy_class_init); + +static void __exit wpan_phy_class_exit(void) +{ + class_unregister(&wpan_phy_class); +} +module_exit(wpan_phy_class_exit); + +MODULE_DESCRIPTION("IEEE 802.15.4 device class"); +MODULE_LICENSE("GPL v2"); + -- cgit v1.1 From 16eea493da563b5a3356a77c6d8776dffc29d3b6 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Wed, 19 Aug 2009 19:32:24 +0400 Subject: ieee802154: add support for channel pages from IEEE 802.15.4-2006 IEEE 802.15.4-2006 adds new concept: channel pages, which can contain several channels. Add support for channel pages in the API and in the fakehard driver. Signed-off-by: Dmitry Eremin-Solenikov --- net/ieee802154/netlink.c | 28 +++++++++++++++++++++++++--- net/ieee802154/nl_policy.c | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index cd0567f..2106ecb 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -232,7 +232,7 @@ nla_put_failure: EXPORT_SYMBOL(ieee802154_nl_beacon_indic); int ieee802154_nl_scan_confirm(struct net_device *dev, - u8 status, u8 scan_type, u32 unscanned, + u8 status, u8 scan_type, u32 unscanned, u8 page, u8 *edl/* , struct list_head *pan_desc_list */) { struct sk_buff *msg; @@ -251,6 +251,7 @@ int ieee802154_nl_scan_confirm(struct net_device *dev, NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); + NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page); if (edl) NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); @@ -349,6 +350,7 @@ static int ieee802154_associate_req(struct sk_buff *skb, { struct net_device *dev; struct ieee802154_addr addr; + u8 page; int ret = -EINVAL; if (!info->attrs[IEEE802154_ATTR_CHANNEL] || @@ -374,8 +376,14 @@ static int ieee802154_associate_req(struct sk_buff *skb, } addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); + if (info->attrs[IEEE802154_ATTR_PAGE]) + page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); + else + page = 0; + ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), + page, nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); dev_put(dev); @@ -458,6 +466,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) struct ieee802154_addr addr; u8 channel, bcn_ord, sf_ord; + u8 page; int pan_coord, blx, coord_realign; int ret; @@ -488,13 +497,19 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); + if (info->attrs[IEEE802154_ATTR_PAGE]) + page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); + else + page = 0; + + if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); dev_put(dev); return -EINVAL; } - ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, + ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, bcn_ord, sf_ord, pan_coord, blx, coord_realign); dev_put(dev); @@ -508,6 +523,7 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) u8 type; u32 channels; u8 duration; + u8 page; if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || !info->attrs[IEEE802154_ATTR_CHANNELS] || @@ -522,7 +538,13 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); - ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, + if (info->attrs[IEEE802154_ATTR_PAGE]) + page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); + else + page = 0; + + + ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, duration); dev_put(dev); diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 83cb4cc..2363ebe 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -33,6 +33,7 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_HW_ADDR] = { .type = NLA_HW_ADDR, }, [IEEE802154_ATTR_PAN_ID] = { .type = NLA_U16, }, [IEEE802154_ATTR_CHANNEL] = { .type = NLA_U8, }, + [IEEE802154_ATTR_PAGE] = { .type = NLA_U8, }, [IEEE802154_ATTR_COORD_SHORT_ADDR] = { .type = NLA_U16, }, [IEEE802154_ATTR_COORD_HW_ADDR] = { .type = NLA_HW_ADDR, }, [IEEE802154_ATTR_COORD_PAN_ID] = { .type = NLA_U16, }, -- cgit v1.1 From 929122cdd5d4c344e59f9b55f870a8fcf7aa0d27 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 14 Aug 2009 20:00:20 +0400 Subject: Drop ARPHRD_IEEE802154_PHY There are not maste devices in mac802154 anymore, so drop ARPHRD_IEEE802154_PHY definition. Signed-off-by: Dmitry Eremin-Solenikov --- net/core/dev.c | 4 ++-- net/ieee802154/af_ieee802154.c | 4 +--- net/ieee802154/raw.c | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 09fb03f..4b83789 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -269,7 +269,7 @@ static const unsigned short netdev_lock_type[] = ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL, ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, - ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_IEEE802154_PHY, + ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE}; static const char *const netdev_lock_name[] = @@ -287,7 +287,7 @@ static const char *const netdev_lock_name[] = "_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL", "_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", - "_xmit_PHONET_PIPE", "_xmit_IEEE802154", "_xmit_IEEE802154_PHY", + "_xmit_PHONET_PIPE", "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"}; static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)]; diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index d504c34..cd949d5 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -147,9 +147,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg, dev_load(sock_net(sk), ifr.ifr_name); dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); - if ((dev->type == ARPHRD_IEEE802154 || - dev->type == ARPHRD_IEEE802154_PHY) && - dev->netdev_ops->ndo_do_ioctl) + if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl) ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd); if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index 60dee69..4681501 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -74,8 +74,7 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len) goto out; } - if (dev->type != ARPHRD_IEEE802154_PHY && - dev->type != ARPHRD_IEEE802154) { + if (dev->type != ARPHRD_IEEE802154) { err = -ENODEV; goto out_put; } -- cgit v1.1 From a8b875e7dc80ff442698d8cf4f45ccce400a6a66 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Thu, 13 Aug 2009 20:39:31 +0300 Subject: cfg80211: export cfg80211_wext_siwfreq cfg80211_wext_siwfreq() should be exported with EXPORT_SYMBOL_GPL. Signed-off-by: Jussi Kivilinna Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c449174..c12029b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -771,6 +771,7 @@ int cfg80211_wext_siwfreq(struct net_device *dev, return err; } } +EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, -- cgit v1.1 From 0ff6ce7b36199f67f709c750e9a2a66659a4babe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 12:25:37 +0200 Subject: cfg80211: fix deadlock When removing an interface with nl80211, cfg80211 will deadlock in the netdev notifier because we're already holding rdev->mtx and try to acquire it again to verify the scan has been done. This bug was introduced by my patch "cfg80211: check for and abort dangling scan requests". To fix this, move the dangling scan request check into wiphy_unregister(). This will not be able to catch all cases right away, but if the scan problem happens with a manual ifdown or so it will be possible to remedy it by removing the module/device. Additionally, add comments about the deadlock scenario. Reported-by: Christian Lamparter Signed-off-by: Johannes Berg Tested-by: Christian Lamparter Tested-by: Kalle Valo Signed-off-by: John W. Linville --- net/wireless/core.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index bc99e4e..69a185b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -586,9 +586,14 @@ void wiphy_unregister(struct wiphy *wiphy) * get to lock contention here if userspace issues a command * that identified the hardware by wiphy index. */ - mutex_lock(&rdev->mtx); - /* unlock again before freeing */ - mutex_unlock(&rdev->mtx); + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + + cfg80211_unlock_rdev(rdev); cfg80211_debugfs_rdev_del(rdev); @@ -605,7 +610,6 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); - kfree(rdev->scan_req); flush_work(&rdev->event_work); } EXPORT_SYMBOL(wiphy_unregister); @@ -653,6 +657,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, switch (state) { case NETDEV_REGISTER: + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are added with nl80211. + */ mutex_init(&wdev->mtx); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); @@ -730,13 +739,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif break; case NETDEV_UNREGISTER: - cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); - } - + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are removed with nl80211. + */ mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER @@ -755,7 +762,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif } mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) -- cgit v1.1 From ea416a793d2b611f22b42ba094fd2e5bd30fff43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 12:22:14 +0200 Subject: cfg80211: report userspace SME connected event properly Instead of hacking the event reporting into the __cfg80211_connect_result() function which is also invoked by others, set up things correctly and then invoke that function, so that it can do more sanity checking. Also, it is currently not possible to get a ROAMED event from the userspace SME anyway since we send out a DISCONNECTED event when it disassociates and then a new CONNECTED event on the next association. Thanks to Zhu Yi for pointing out that the code is somewhat convoluted and doesn't warn when it should. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 9 +++++++++ net/wireless/sme.c | 22 ++++++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index da64071..79d2eec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -96,6 +96,15 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) WARN_ON(!bss); } + if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { + /* + * This is for the userspace SME, the CONNECTING + * state will be changed to CONNECTED by + * __cfg80211_connect_result() below. + */ + wdev->sme_state = CFG80211_SME_CONNECTING; + } + /* this consumes one bss reference (unless bss is NULL) */ __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8e2ef54..6fb6a70 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -351,15 +351,13 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (wdev->sme_state == CFG80211_SME_CONNECTED) - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + return; + + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, GFP_KERNEL); - else - nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, GFP_KERNEL); + resp_ie, resp_ie_len, + status, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (wextev) { @@ -392,13 +390,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->current_bss = NULL; } - if (status == WLAN_STATUS_SUCCESS && - wdev->sme_state == CFG80211_SME_IDLE) - goto success; - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; @@ -412,7 +403,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; } - success: if (!bss) bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, -- cgit v1.1 From 3ac64beecd27400d12cc7afb4108eef26c499f6a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 16:16:53 +0200 Subject: mac80211: allow configure_filter callback to sleep Over time, a whole bunch of drivers have come up with their own scheme to delay the configure_filter operation to a workqueue. To be able to simplify things, allow configure_filter to sleep, and add a new prepare_multicast callback that drivers that need the multicast address list implement. This new callback must be atomic, but most drivers either don't care or just calculate a hash which can be done atomically and then uploaded to the hardware non-atomically. A cursory look suggests that at76c50x-usb, ar9170, mwl8k (which is actually very broken now), rt2x00, wl1251, wl1271 and zd1211 should make use of this new capability. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/driver-ops.h | 24 ++++++++++++++++++++---- net/mac80211/driver-trace.h | 36 ++++++++++++++++++++++++++++++------ net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/iface.c | 15 +++------------ net/mac80211/main.c | 24 +++++++++++++++++++----- net/mac80211/scan.c | 16 ++-------------- net/mac80211/util.c | 2 -- 7 files changed, 77 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4100c36..d231c93 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -55,16 +55,32 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_bss_info_changed(local, vif, info, changed); } +static inline u64 drv_prepare_multicast(struct ieee80211_local *local, + int mc_count, + struct dev_addr_list *mc_list) +{ + u64 ret = 0; + + if (local->ops->prepare_multicast) + ret = local->ops->prepare_multicast(&local->hw, mc_count, + mc_list); + + trace_drv_prepare_multicast(local, mc_count, ret); + + return ret; +} + static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + u64 multicast) { + might_sleep(); + local->ops->configure_filter(&local->hw, changed_flags, total_flags, - mc_count, mc_list); + multicast); trace_drv_configure_filter(local, changed_flags, total_flags, - mc_count); + multicast); } static inline int drv_set_tim(struct ieee80211_local *local, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 5a10da2..37b9051 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -191,31 +191,55 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +TRACE_EVENT(drv_prepare_multicast, + TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret), + + TP_ARGS(local, mc_count, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, mc_count) + __field(u64, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->mc_count = mc_count; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " prepare mc (%d): %llx", + LOCAL_PR_ARG, __entry->mc_count, + (unsigned long long) __entry->ret + ) +); + TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count), + u64 multicast), - TP_ARGS(local, changed_flags, total_flags, mc_count), + TP_ARGS(local, changed_flags, total_flags, multicast), TP_STRUCT__entry( LOCAL_ENTRY __field(unsigned int, changed) __field(unsigned int, total) - __field(int, mc) + __field(u64, multicast) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed_flags; __entry->total = *total_flags; - __entry->mc = mc_count; + __entry->multicast = multicast; ), TP_printk( - LOCAL_PR_FMT " changed:%#x total:%#x mc:%d", - LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc + LOCAL_PR_FMT " changed:%#x total:%#x", + LOCAL_PR_ARG, __entry->changed, __entry->total ) ); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a6abc7d..a07f017 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -636,6 +636,9 @@ struct ieee80211_local { /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; + /* used for uploading changed mc list */ + struct work_struct reconfig_filter; + /* aggregated multicast list */ struct dev_addr_list *mc_list; int mc_count; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e8fb03b..b161301 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -227,9 +227,7 @@ static int ieee80211_open(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; default: conf.vif = &sdata->vif; @@ -241,17 +239,13 @@ static int ieee80211_open(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) { local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); } else if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); } changed |= ieee80211_reset_erp_info(sdata); @@ -404,10 +398,11 @@ static int ieee80211_stop(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_unsync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(dev); + ieee80211_configure_filter(local); + del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -458,9 +453,7 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); @@ -503,9 +496,7 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; atomic_dec(&local->iff_allmultis); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_stop_mesh(sdata); } @@ -622,8 +613,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_sync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); + ieee80211_queue_work(&local->hw, &local->reconfig_filter); } /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b03fd84..05f9235 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr { } __attribute__ ((packed)); -/* must be called under mdev tx lock */ void ieee80211_configure_filter(struct ieee80211_local *local) { + u64 mc; unsigned int changed_flags; unsigned int new_flags = 0; @@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors) + if (local->monitors || local->scanning) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_fcsfail) @@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (local->fif_pspoll) new_flags |= FIF_PSPOLL; + spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; + mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + spin_unlock_bh(&local->filter_lock); + /* be a bit nasty */ new_flags |= (1<<31); - drv_configure_filter(local, changed_flags, &new_flags, - local->mc_count, - local->mc_list); + drv_configure_filter(local, changed_flags, &new_flags, mc); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } +static void ieee80211_reconfig_filter(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, reconfig_filter); + + ieee80211_configure_filter(local); +} + int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -692,6 +702,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); + INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); + INIT_WORK(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); INIT_WORK(&local->dynamic_ps_disable_work, @@ -946,6 +958,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) rtnl_unlock(); + cancel_work_sync(&local->reconfig_filter); + ieee80211_clear_tx_pending(local); sta_info_stop(local); rate_control_deinitialize(local); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index e091cbc..1e04be6 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -292,13 +292,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (was_hw_scan) goto done; - spin_lock_bh(&local->filter_lock); - local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); drv_sw_scan_complete(local); @@ -376,13 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - spin_lock_bh(&local->filter_lock); - local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); /* TODO: start scan as soon as all nullfunc frames are ACKed */ ieee80211_queue_delayed_work(&local->hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e55d57f..5eb3063 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1076,9 +1076,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* reconfigure hardware */ ieee80211_hw_config(local, ~0); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { -- cgit v1.1 From f424afa17899408cbd267a4c4534ca6fc9d8f71c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 16:18:07 +0200 Subject: mac80211: remove deprecated API All but two drivers have now stopped using the two deprecated members radio_enabled and beacon_int, so it's about time to remove them for good. Signed-off-by: Johannes Berg Acked-by: Kalle Valo Signed-off-by: John W. Linville --- net/mac80211/main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 05f9235..3302df9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -241,9 +241,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, drv_bss_info_changed(local, &sdata->vif, &sdata->vif.bss_conf, changed); - - /* DEPRECATED */ - local->hw.conf.beacon_int = sdata->vif.bss_conf.beacon_int; } u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) @@ -687,7 +684,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.max_rates = 1; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; - local->hw.conf.radio_enabled = true; local->user_power_level = -1; INIT_LIST_HEAD(&local->interfaces); -- cgit v1.1 From bfc32e6a9559d3e30925929cd9a9df7498f325db Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 17 Aug 2009 17:15:55 -0700 Subject: mac80211: Decouple fail_avg stats used by mesh from rate control algorithm. Mesh uses the tx failure average to compute the (m)path metric. This used to be done inside the rate control module. This patch breaks the dependency between the mesh stack and the rate control algorithm. Mesh will now work independently of the chosen rate control algorithm. The mesh stack keeps a moving average of the average transmission losses for each mesh peer station. If the fail average exceeds a certain threshold, the peer link is marked as broken. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 ++ net/mac80211/mesh.h | 2 ++ net/mac80211/mesh_hwmp.c | 18 ++++++++++++++++++ net/mac80211/rc80211_minstrel.c | 16 +--------------- net/mac80211/rc80211_pid_algo.c | 15 +-------------- 5 files changed, 24 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 3302df9..f80efd7 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -482,6 +482,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } rate_control_tx_status(local, sband, sta, skb); + if (ieee80211_vif_is_mesh(&sta->sdata->vif)) + ieee80211s_update_metric(local, sta, skb); } rcu_read_unlock(); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index eb23fc6..dd1c193 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -226,6 +226,8 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index ef1efd3..7aeba00 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -201,6 +201,24 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra, return 0; } +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int failed; + + if (!ieee80211_is_data(hdr->frame_control)) + return; + + failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); + + /* moving average, scaled to 100 */ + stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed); + if (stainfo->fail_avg > 95) + mesh_plink_broken(stainfo); +} + static u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta) { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 0071649..7c51429 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -51,7 +51,6 @@ #include #include #include -#include "mesh.h" #include "rate.h" #include "rc80211_minstrel.h" @@ -156,16 +155,12 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, struct sk_buff *skb) { struct minstrel_sta_info *mi = priv_sta; - struct minstrel_priv *mp = (struct minstrel_priv *)priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; - struct ieee80211_local *local = hw_to_local(mp->hw); - struct sta_info *si; int i, ndx; int success; success = !!(info->flags & IEEE80211_TX_STAT_ACK); - si = sta_info_get(local, sta->addr); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (ar[i].idx < 0) @@ -177,17 +172,8 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, mi->r[ndx].attempts += ar[i].count; - if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) { + if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) mi->r[ndx].success += success; - if (si) { - si->fail_avg = (18050 - mi->r[ndx].probability) - / 180; - WARN_ON(si->fail_avg > 100); - if (si->fail_avg == 100 && - ieee80211_vif_is_mesh(&si->sdata->vif)) - mesh_plink_broken(si); - } - } } if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 8c053be..f6e25d7 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -169,19 +169,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo, * still a good measurement and copy it. */ if (unlikely(spinfo->tx_num_xmit == 0)) pf = spinfo->last_pf; - else { - /* XXX: BAD HACK!!! */ - struct sta_info *si = container_of(sta, struct sta_info, sta); - + else pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; - if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100) - mesh_plink_broken(si); - pf <<= RC_PID_ARITH_SHIFT; - si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9) - >> RC_PID_ARITH_SHIFT; - } - spinfo->tx_num_xmit = 0; spinfo->tx_num_failed = 0; @@ -348,9 +338,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, } spinfo->txrate_idx = rate_lowest_index(sband, sta); - /* HACK */ - si = container_of(sta, struct sta_info, sta); - si->fail_avg = 0; } static void *rate_control_pid_alloc(struct ieee80211_hw *hw, -- cgit v1.1 From 29508d122a5228c2a68d1e9a39251d3991b3cfef Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 18 Aug 2009 10:46:42 -0400 Subject: rc80211_pid_algo.c: remove unused variable declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC [M] net/mac80211/rc80211_pid_algo.o net/mac80211/rc80211_pid_algo.c: In function ‘rate_control_pid_rate_init’: net/mac80211/rc80211_pid_algo.c:304: warning: unused variable ‘si’ Signed-off-by: John W. Linville --- net/mac80211/rc80211_pid_algo.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index f6e25d7..699d3ed 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -301,7 +301,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, struct rc_pid_sta_info *spinfo = priv_sta; struct rc_pid_info *pinfo = priv; struct rc_pid_rateinfo *rinfo = pinfo->rinfo; - struct sta_info *si; int i, j, tmp; bool s; -- cgit v1.1 From c8a61a7d33350eeec668fc6230ad55f5fa93209b Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Tue, 18 Aug 2009 10:59:00 -0700 Subject: mac80211: New stat counters for multicast and unicast forwarded frames This expands on the current fwded_frames stat counter which should be equal to the total of these two new counters. The new counters are called "fwded_mcast" and "fwded_unicast". Signed-off-by: Daniel Walker Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/debugfs_netdev.c | 6 ++++++ net/mac80211/ieee80211_i.h | 6 +++++- net/mac80211/mesh_hwmp.c | 3 +++ net/mac80211/rx.c | 8 +++++++- 4 files changed, 21 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e9ec6ca..61234e7 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -116,6 +116,8 @@ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); #ifdef CONFIG_MAC80211_MESH /* Mesh stats attributes */ +IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); +IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, @@ -205,6 +207,8 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats", sdata->debugfsdir); + MESHSTATS_ADD(fwded_mcast); + MESHSTATS_ADD(fwded_unicast); MESHSTATS_ADD(fwded_frames); MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); @@ -327,6 +331,8 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata) static void del_mesh_stats(struct ieee80211_sub_if_data *sdata) { + MESHSTATS_DEL(fwded_mcast); + MESHSTATS_DEL(fwded_unicast); MESHSTATS_DEL(fwded_frames); MESHSTATS_DEL(dropped_frames_ttl); MESHSTATS_DEL(dropped_frames_no_route); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a07f017..93e618a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -212,7 +212,9 @@ struct ieee80211_if_vlan { }; struct mesh_stats { - __u32 fwded_frames; /* Mesh forwarded frames */ + __u32 fwded_mcast; /* Mesh forwarded multicast frames */ + __u32 fwded_unicast; /* Mesh forwarded unicast frames */ + __u32 fwded_frames; /* Mesh total forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ atomic_t estab_plinks; @@ -506,6 +508,8 @@ struct ieee80211_sub_if_data { #ifdef CONFIG_MAC80211_MESH struct dentry *mesh_stats_dir; struct { + struct dentry *fwded_mcast; + struct dentry *fwded_unicast; struct dentry *fwded_frames; struct dentry *dropped_frames_ttl; struct dentry *dropped_frames_no_route; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 7aeba00..e12a786 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -497,6 +497,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), cpu_to_le32(preq_id), sdata); + ifmsh->mshstats.fwded_mcast++; ifmsh->mshstats.fwded_frames++; } } @@ -555,6 +556,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); rcu_read_unlock(); + + sdata->u.mesh.mshstats.fwded_unicast++; sdata->u.mesh.mshstats.fwded_frames++; return; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4cd9e45..7065fd7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1550,7 +1550,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; ieee80211_select_queue(local, fwd_skb); - if (!is_multicast_ether_addr(fwd_hdr->addr1)) { + if (is_multicast_ether_addr(fwd_hdr->addr1)) + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_mcast); + else { int err; /* * Save TA to addr1 to send TA a path error if a @@ -1564,6 +1567,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) * later to the pending skb queue. */ if (err) return RX_DROP_MONITOR; + + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_unicast); } IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_frames); -- cgit v1.1 From ad002395fd230528281083f4be71855ed7e35b04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Aug 2009 19:51:57 +0200 Subject: cfg80211: fix dangling scan request checking My patch "cfg80211: fix deadlock" broke the code it was supposed to fix, the scan request checking. But it's not trivial to put it back the way it was, since the original patch had a deadlock. Now do it in a completely new way: queue the check off to a work struct, where we can freely lock. But that has some more complications, like needing to wait for it to be done before the wiphy/rdev can be destroyed, so some code is required to handle that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 76 ++++++++++++++++++++++++++++++++++++++++++++--------- net/wireless/core.h | 2 ++ 2 files changed, 65 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 69a185b..c150071 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -430,6 +430,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_WORK(&rdev->conn_work, cfg80211_conn_work); INIT_WORK(&rdev->event_work, cfg80211_event_work); + init_waitqueue_head(&rdev->dev_wait); + /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the @@ -574,7 +576,23 @@ void wiphy_unregister(struct wiphy *wiphy) /* protect the device list */ mutex_lock(&cfg80211_mutex); + wait_event(rdev->dev_wait, ({ + int __count; + mutex_lock(&rdev->devlist_mtx); + __count = rdev->opencount; + mutex_unlock(&rdev->devlist_mtx); + __count == 0;})); + + mutex_lock(&rdev->devlist_mtx); BUG_ON(!list_empty(&rdev->netdev_list)); + mutex_unlock(&rdev->devlist_mtx); + + /* + * First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + cfg80211_debugfs_rdev_del(rdev); + list_del(&rdev->list); /* * Try to grab rdev->mtx. If a command is still in progress, @@ -582,26 +600,18 @@ void wiphy_unregister(struct wiphy *wiphy) * down the device already. We wait for this command to complete * before unlinking the item from the list. * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still associated. Hence, we can only - * get to lock contention here if userspace issues a command - * that identified the hardware by wiphy index. + * a virtual interface is still present. Hence, we can only get + * to lock contention here if userspace issues a command that + * identified the hardware by wiphy index. */ cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); - } - + /* nothing */ cfg80211_unlock_rdev(rdev); - cfg80211_debugfs_rdev_del(rdev); - /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&rdev->list); cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); debugfs_remove(rdev->wiphy.debugfsdir); @@ -640,6 +650,31 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); +static void wdev_cleanup_work(struct work_struct *work) +{ + struct wireless_dev *wdev; + struct cfg80211_registered_device *rdev; + + wdev = container_of(work, struct wireless_dev, cleanup_work); + rdev = wiphy_to_dev(wdev->wiphy); + + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + + cfg80211_unlock_rdev(rdev); + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + wake_up(&rdev->dev_wait); + + dev_put(wdev->netdev); +} + static int cfg80211_netdev_notifier_call(struct notifier_block * nb, unsigned long state, void *ndev) @@ -663,6 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, * are added with nl80211. */ mutex_init(&wdev->mtx); + INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); @@ -717,8 +753,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + dev_hold(dev); + schedule_work(&wdev->cleanup_work); break; case NETDEV_UP: + /* + * If we have a really quick DOWN/UP succession we may + * have this work still pending ... cancel it and see + * if it was pending, in which case we need to account + * for some of the work it would have done. + */ + if (cancel_work_sync(&wdev->cleanup_work)) { + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + dev_put(dev); + } #ifdef CONFIG_WIRELESS_EXT cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); @@ -734,6 +784,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } wdev_unlock(wdev); + rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); #endif @@ -756,7 +807,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); rdev->devlist_generation++; - mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.keys); #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index c603f52..f565432 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -50,6 +50,8 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; int devlist_generation; + int opencount; /* also protected by devlist_mtx */ + wait_queue_head_t dev_wait; /* BSSes/scanning */ spinlock_t bss_lock; -- cgit v1.1 From 415ad1efae1d5fe00d739e612d262eabda90f5e8 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Wed, 19 Aug 2009 00:43:31 +0100 Subject: cfg80211: fix leaks of wdev->conn->ie This only occurs in the following error situations: - driver calls connect_result with failure - error scheduling authentication on connect - error initiating scan (to get BSSID and channel) on connect - userspace calls disconnect while in the SCANNING or SCAN_AGAIN states Signed-off-by: David Kilroy Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 6fb6a70..9ddc00e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -395,6 +395,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; + if (wdev->conn) + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; kfree(wdev->connect_keys); @@ -779,6 +781,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, } } if (err) { + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; @@ -848,6 +851,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, (wdev->conn->state == CFG80211_CONN_SCANNING || wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->ssid_len = 0; -- cgit v1.1 From 16a832e785820aa199641c77b2d6f4a443d2ec46 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Wed, 19 Aug 2009 16:08:22 +0800 Subject: cfg80211: allow cfg80211_connect_result with bssid == NULL In case of connection failure, the bssid info is not a must have. Signed-off-by: Zhu Yi Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +++- net/wireless/sme.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index c150071..154e1e2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -309,7 +310,8 @@ static void cfg80211_process_events(struct wireless_dev *wdev) switch (ev->type) { case EVENT_CONNECT_RESULT: __cfg80211_connect_result( - wdev->netdev, ev->cr.bssid, + wdev->netdev, is_zero_ether_addr(ev->cr.bssid) ? + NULL : ev->cr.bssid, ev->cr.req_ie, ev->cr.req_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.status, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 9ddc00e..4a8289f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -450,7 +450,8 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; ev->type = EVENT_CONNECT_RESULT; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); + if (bssid) + memcpy(ev->cr.bssid, bssid, ETH_ALEN); ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); ev->cr.req_ie_len = req_ie_len; memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); -- cgit v1.1 From 11ba964d4f936609a04e8b9f2051f6027ef761ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Aug 2009 19:45:50 +0200 Subject: mac80211: fix register_hw error path "cfg80211: fix alignment problem in scan request" introduced a bug into the error path, because now we allocate the entire scan request and not just the channel list (the channel list is allocated together with the scan request) -- on errors we thus also need to free the entire scan request. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f80efd7..dd3b081 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -930,7 +930,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: - kfree(local->int_scan_req->channels); + kfree(local->int_scan_req); return result; } EXPORT_SYMBOL(ieee80211_register_hw); -- cgit v1.1 From fd0b3ff707dc1f7837079044bd4eca7ed505f70d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 16 Jun 2009 00:01:49 +0200 Subject: Bluetooth: Add proper shutdown support to SCO sockets The SCO sockets for Bluetooth audio setup and streaming are missing the shutdown implementation. This hasn't been a problem so far, but with a more deeper integration with PulseAudio it is important to shutdown SCO sockets properly. Also the Headset profile 1.2 has more detailed qualification tests that require that SCO and RFCOMM channels are terminated in the right order. A proper shutdown function is necessary for this. Based on a report by Johan Hedberg Signed-off-by: Marcel Holtmann Tested-by: Johan Hedberg --- net/bluetooth/sco.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 51ae0c3..13c27f1 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk) sock_put(sk); } -/* Close socket. - * Must be called on unlocked socket. - */ -static void sco_sock_close(struct sock *sk) +static void __sco_sock_close(struct sock *sk) { - struct sco_conn *conn; - - sco_sock_clear_timer(sk); - - lock_sock(sk); - - conn = sco_pi(sk)->conn; - - BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket); + BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); switch (sk->sk_state) { case BT_LISTEN: @@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk) sock_set_flag(sk, SOCK_ZAPPED); break; } +} +/* Must be called on unlocked socket. */ +static void sco_sock_close(struct sock *sk) +{ + sco_sock_clear_timer(sk); + lock_sock(sk); + __sco_sock_close(sk); release_sock(sk); - sco_sock_kill(sk); } @@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char return err; } +static int sco_sock_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + lock_sock(sk); + if (!sk->sk_shutdown) { + sk->sk_shutdown = SHUTDOWN_MASK; + sco_sock_clear_timer(sk); + __sco_sock_close(sk); + + if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) + err = bt_sock_wait_state(sk, BT_CLOSED, + sk->sk_lingertime); + } + release_sock(sk); + return err; +} + static int sco_sock_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = { .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, - .shutdown = sock_no_shutdown, + .shutdown = sco_sock_shutdown, .setsockopt = sco_sock_setsockopt, .getsockopt = sco_sock_getsockopt }; -- cgit v1.1 From 981b1414d78a7a42cab48b97d4de54a62d61db88 Mon Sep 17 00:00:00 2001 From: Vikram Kandukuri Date: Wed, 1 Jul 2009 11:39:58 +0530 Subject: Bluetooth: Fix missing scheduling when VIRTUAL_CABLE_UNPLUG is received There is a test case in PTS tool; PTS will send the VIRTUAL_CABLE_UNPLUG command to IUT. Then IUT should disconnect the channel and kill the HID session when it receives the command. The VIRTUAL_CABLE_UNPLUG command is parsed by HID transport, but it is not scheduled to do so. Add a call to hidp_schedule() to kill the session. Signed-off-by: Jothikumar Mothilal Signed-off-by: Marcel Holtmann --- net/bluetooth/hidp/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b186768..a9f7afb 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -374,6 +374,7 @@ static void hidp_process_hid_control(struct hidp_session *session, /* Kill session thread */ atomic_inc(&session->terminate); + hidp_schedule(session); } } -- cgit v1.1 From 364f63519d94442ed373ac7da79033c8282df46a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 22 Aug 2009 14:15:53 -0700 Subject: Bluetooth: Disconnect HIDRAW devices on disconnect Currently the HID subsystem will create HIDRAW devices for the transport driver, but it will not disconnect them. Until the HID subsytem gets fixed, ensure that HIDRAW and HIDDEV devices are disconnected when the Bluetooth HID device gets removed. Based on a patch from Brian Rogers Signed-off-by: Marcel Holtmann --- net/bluetooth/hidp/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index a9f7afb..f912d65 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -574,6 +575,8 @@ static int hidp_session(void *arg) if (session->hid) { if (session->hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(session->hid); + if (session->hid->claimed & HID_CLAIMED_HIDRAW) + hidraw_disconnect(session->hid); hid_destroy_device(session->hid); } -- cgit v1.1 From 9eba32b86d17ef87131fa0bce43c614904ab5781 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 22 Aug 2009 14:19:26 -0700 Subject: Bluetooth: Add extra device reference counting for connections The device model itself has no real usable reference counting at the moment and this causes problems if parents are deleted before their children. The device model itself handles the memory details of this correctly, but the uevent order is not consistent. This causes various problems for systems like HAL or even X. So until device_put() does a proper cleanup, the device for Bluetooth connection will be protected with an extra reference counting to ensure the correct order of uevents when connections are terminated. This is not an automatic feature. Higher Bluetooth layers like HIDP or BNEP should grab this new reference to ensure that their uevents are send before the ones from the parent device. Based on a report by Brian Rogers Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 17 ++++++++++++++++- net/bluetooth/hci_event.c | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index fa47d5d..a975098 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); + atomic_set(&conn->devref, 0); + hci_conn_init_sysfs(conn); tasklet_enable(&hdev->tx_task); @@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn) skb_queue_purge(&conn->data_q); - hci_conn_del_sysfs(conn); + hci_conn_put_device(conn); hci_dev_put(hdev); @@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev) hci_dev_unlock(hdev); } +void hci_conn_hold_device(struct hci_conn *conn) +{ + atomic_inc(&conn->devref); +} +EXPORT_SYMBOL(hci_conn_hold_device); + +void hci_conn_put_device(struct hci_conn *conn) +{ + if (atomic_dec_and_test(&conn->devref)) + hci_conn_del_sysfs(conn); +} +EXPORT_SYMBOL(hci_conn_put_device); + int hci_get_conn_list(void __user *arg) { struct hci_conn_list_req req, *cl; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 184ba0a..e99fe38 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s } else conn->state = BT_CONNECTED; + hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) @@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; + hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); break; -- cgit v1.1 From edad63886993d18ab800c49f6587a93432ef8b35 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 22 Aug 2009 14:22:15 -0700 Subject: Bluetooth: Let HIDP grab the device reference for connections The core exports the hci_conn_hold_device() and hci_conn_put_device() functions for device reference of connections. Use this to ensure that the uevents from the parent are send after the child ones. Based on a report by Brian Rogers Signed-off-by: Marcel Holtmann --- net/bluetooth/hidp/core.c | 62 +++++++++++++++++++++++++++++++---------------- net/bluetooth/hidp/hidp.h | 2 ++ 2 files changed, 43 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index f912d65..09bedeb 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -93,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session) { __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); + + hci_conn_hold_device(session->conn); } static void __hidp_unlink_session(struct hidp_session *session) { + hci_conn_put_device(session->conn); + list_del(&session->list); module_put(THIS_MODULE); } @@ -577,7 +581,9 @@ static int hidp_session(void *arg) hidinput_disconnect(session->hid); if (session->hid->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(session->hid); + hid_destroy_device(session->hid); + session->hid = NULL; } /* Wakeup user-space polling for socket errors */ @@ -605,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session) { bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; + struct device *device = NULL; struct hci_dev *hdev; - struct hci_conn *conn; hdev = hci_get_route(dst, src); if (!hdev) return NULL; - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (session->conn) + device = &session->conn->dev; hci_dev_put(hdev); - return conn ? &conn->dev : NULL; + return device; } static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { struct input_dev *input; - int i; + int err, i; input = input_allocate_device(); if (!input) @@ -670,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session, input->event = hidp_input_event; - return input_register_device(input); + err = input_register_device(input); + if (err < 0) { + hci_conn_put_device(session->conn); + return err; + } + + return 0; } static int hidp_open(struct hid_device *hid) @@ -752,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session, { struct hid_device *hid; bdaddr_t src, dst; - int ret; + int err; hid = hid_allocate_device(); - if (IS_ERR(hid)) { - ret = PTR_ERR(session->hid); - goto err; - } + if (IS_ERR(hid)) + return PTR_ERR(session->hid); session->hid = hid; session->req = req; @@ -780,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session, hid->dev.parent = hidp_get_device(session); hid->ll_driver = &hidp_hid_driver; - ret = hid_add_device(hid); - if (ret) - goto err_hid; + err = hid_add_device(hid); + if (err < 0) + goto failed; return 0; -err_hid: + +failed: hid_destroy_device(hid); session->hid = NULL; -err: - return ret; + + return err; } int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) @@ -839,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err && err != -ENODEV) - goto err_skb; + goto purge; } if (!session->hid) { err = hidp_setup_input(session, req); if (err < 0) - goto err_skb; + goto purge; } __hidp_link_session(session); @@ -873,13 +886,20 @@ unlink: __hidp_unlink_session(session); - if (session->input) + if (session->input) { input_unregister_device(session->input); - if (session->hid) + session->input = NULL; + } + + if (session->hid) { hid_destroy_device(session->hid); -err_skb: + session->hid = NULL; + } + +purge: skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); + failed: up_write(&hidp_session_sem); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index e503c89..faf3d74 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci); struct hidp_session { struct list_head list; + struct hci_conn *conn; + struct socket *ctrl_sock; struct socket *intr_sock; -- cgit v1.1 From a6a67efd7088702fdbbb780c5a3f8e1a74e77b63 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 26 Jul 2009 08:18:19 +0000 Subject: Bluetooth: Convert hdev->req_lock to a mutex hdev->req_lock is used as mutex so make it a mutex. Signed-off-by: Thomas Gleixner Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 406ad07..e1da8f6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev) hdev->reassembly[i] = NULL; init_waitqueue_head(&hdev->req_wait_q); - init_MUTEX(&hdev->req_lock); + mutex_init(&hdev->req_lock); inquiry_cache_init(hdev); -- cgit v1.1 From 52d18347dfb61519aa0f58fe1759edd3ad8c4e36 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 22 Aug 2009 14:49:36 -0700 Subject: Bluetooth: Coding style cleanup from previous rfcomm_init bug fix The rfcomm_init bug fix went into the kernel premature before it got fully reviewed and acknowledged by the Bluetooth maintainer. So fix up the coding style now. Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/core.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 94b3388..26af485 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -2080,7 +2080,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL); /* ---- Initialization ---- */ static int __init rfcomm_init(void) { - int ret; + int err; l2cap_load(); @@ -2088,33 +2088,35 @@ static int __init rfcomm_init(void) rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); if (IS_ERR(rfcomm_thread)) { - ret = PTR_ERR(rfcomm_thread); - goto out_thread; + err = PTR_ERR(rfcomm_thread); + goto unregister; } if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0) BT_ERR("Failed to create RFCOMM info file"); - ret = rfcomm_init_ttys(); - if (ret) - goto out_tty; + err = rfcomm_init_ttys(); + if (err < 0) + goto stop; - ret = rfcomm_init_sockets(); - if (ret) - goto out_sock; + err = rfcomm_init_sockets(); + if (err < 0) + goto cleanup; BT_INFO("RFCOMM ver %s", VERSION); return 0; -out_sock: +cleanup: rfcomm_cleanup_ttys(); -out_tty: + +stop: kthread_stop(rfcomm_thread); -out_thread: + +unregister: hci_unregister_cb(&rfcomm_cb); - return ret; + return err; } static void __exit rfcomm_exit(void) -- cgit v1.1 From 44dd46de325c4d47abfd1361e5d84a548edb8e42 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 May 2009 19:09:01 -0700 Subject: Bluetooth: Add module option to enable L2CAP ERTM support Since the Enhanced Retransmission mode for L2CAP is still under heavy development disable it by default and provide a module option to enable it manually for testing. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index bd0a4c1..810a3c1 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -50,7 +50,9 @@ #include #include -#define VERSION "2.13" +#define VERSION "2.14" + +static int enable_ertm = 0; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; @@ -2205,10 +2207,13 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm if (type == L2CAP_IT_FEAT_MASK) { u8 buf[8]; + u32 feat_mask = l2cap_feat_mask; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data); + if (enable_ertm) + feat_mask |= L2CAP_FEAT_ERTM; + put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else if (type == L2CAP_IT_FIXED_CHAN) { @@ -2828,6 +2833,9 @@ EXPORT_SYMBOL(l2cap_load); module_init(l2cap_init); module_exit(l2cap_exit); +module_param(enable_ertm, bool, 0644); +MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode"); + MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); MODULE_VERSION(VERSION); -- cgit v1.1 From c6b03cf986eab00e20d0dbc852b233bb83472138 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 May 2009 22:31:10 -0700 Subject: Bluetooth: Allow setting of L2CAP ERTM via socket option To enable Enhanced Retransmission mode it needs to be set via a socket option. A different mode can be set on a socket, but on listen() and connect() the mode is checked and ERTM is only allowed if it is enabled via the module parameter. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 810a3c1..8a59e57 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -717,12 +717,16 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; + pi->mode = l2cap_pi(parent)->mode; + pi->fcs = l2cap_pi(parent)->fcs; 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->mode = L2CAP_MODE_BASIC; + pi->fcs = L2CAP_FCS_CRC16; pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; pi->force_reliable = 0; @@ -958,6 +962,18 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al goto done; } + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + if (enable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + switch (sk->sk_state) { case BT_CONNECT: case BT_CONNECT2: @@ -1009,6 +1025,18 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) goto done; } + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + if (enable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + if (!l2cap_pi(sk)->psm) { bdaddr_t *src = &bt_sk(sk)->src; u16 psm; @@ -1259,7 +1287,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = L2CAP_MODE_BASIC; + opts.mode = l2cap_pi(sk)->mode; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -1267,8 +1295,9 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - l2cap_pi(sk)->imtu = opts.imtu; - l2cap_pi(sk)->omtu = opts.omtu; + l2cap_pi(sk)->imtu = opts.imtu; + l2cap_pi(sk)->omtu = opts.omtu; + l2cap_pi(sk)->mode = opts.mode; break; case L2CAP_LM: @@ -1381,7 +1410,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = L2CAP_MODE_BASIC; + opts.mode = l2cap_pi(sk)->mode; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) -- cgit v1.1 From 65c7c4918450f8c4545ccb02a9c7a3d77e073535 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 May 2009 23:07:53 -0700 Subject: Bluetooth: Add L2CAP RFC option if ERTM is enabled When trying to establish a connection with Enhanced Retransmission mode enabled, the RFC option needs to be added to the configuration. Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 8a59e57..7ce1a24 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1743,12 +1743,29 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; + struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; void *ptr = req->data; BT_DBG("sk %p", sk); - if (pi->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + switch (pi->mode) { + case L2CAP_MODE_BASIC: + if (pi->imtu != L2CAP_DEFAULT_MTU) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + break; + + case L2CAP_MODE_ERTM: + rfc.mode = L2CAP_MODE_ERTM; + rfc.txwin_size = L2CAP_DEFAULT_RX_WINDOW; + rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE; + rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + break; + } /* FIXME: Need actual value of the flush timeout */ //if (flush_to != L2CAP_DEFAULT_FLUSH_TO) @@ -1828,7 +1845,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) rfc.mode = L2CAP_MODE_BASIC; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + sizeof(rfc), (unsigned long) &rfc); } } -- cgit v1.1 From f2fcfcd670257236ebf2088bbdf26f6a8ef459fe Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 4 Jul 2009 15:06:24 -0300 Subject: Bluetooth: Add configuration support for ERTM and Streaming mode Add support to config_req and config_rsp to configure ERTM and Streaming mode. If the remote device specifies ERTM or Streaming mode, then the same mode is proposed. Otherwise ERTM or Basic mode is used. And in case of a state 2 device, the remote device should propose the same mode. If not, then the channel gets disconnected. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 262 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 234 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7ce1a24..af0fbf9 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -966,6 +966,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: if (enable_ertm) break; /* fall through */ @@ -1029,6 +1030,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: if (enable_ertm) break; /* fall through */ @@ -1739,15 +1741,65 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) *ptr += L2CAP_CONF_OPT_SIZE + len; } +static int l2cap_mode_supported(__u8 mode, __u32 feat_mask) +{ + u32 local_feat_mask = l2cap_feat_mask; + if (enable_ertm) + local_feat_mask |= L2CAP_FEAT_ERTM; + + switch (mode) { + case L2CAP_MODE_ERTM: + return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; + case L2CAP_MODE_STREAMING: + return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; + default: + return 0x00; + } +} + +static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) +{ + switch (mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + if (l2cap_mode_supported(mode, remote_feat_mask)) + return mode; + /* fall through */ + default: + return L2CAP_MODE_BASIC; + } +} + static int l2cap_build_conf_req(struct sock *sk, void *data) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; - struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; + struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_ERTM }; void *ptr = req->data; BT_DBG("sk %p", sk); + if (pi->num_conf_req || pi->num_conf_rsp) + goto done; + + switch (pi->mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) { + struct l2cap_disconn_req req; + req.dcid = cpu_to_le16(pi->dcid); + req.scid = cpu_to_le16(pi->scid); + l2cap_send_cmd(pi->conn, l2cap_get_ident(pi->conn), + L2CAP_DISCONN_REQ, sizeof(req), &req); + } + break; + default: + pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); + break; + } + +done: switch (pi->mode) { case L2CAP_MODE_BASIC: if (pi->imtu != L2CAP_DEFAULT_MTU) @@ -1756,10 +1808,22 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; - rfc.txwin_size = L2CAP_DEFAULT_RX_WINDOW; + rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE; - rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + rfc.retrans_timeout = 0; + rfc.monitor_timeout = 0; + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + break; + + case L2CAP_MODE_STREAMING: + rfc.mode = L2CAP_MODE_STREAMING; + rfc.txwin_size = 0; + rfc.max_transmit = 0; + rfc.retrans_timeout = 0; + rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, @@ -1825,30 +1889,83 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) } } + if (pi->num_conf_rsp || pi->num_conf_req) + goto done; + + switch (pi->mode) { + case L2CAP_MODE_STREAMING: + case L2CAP_MODE_ERTM: + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) + return -ECONNREFUSED; + break; + default: + pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); + break; + } + +done: + if (pi->mode != rfc.mode) { + result = L2CAP_CONF_UNACCEPT; + rfc.mode = pi->mode; + + if (pi->num_conf_rsp == 1) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + } + + if (result == L2CAP_CONF_SUCCESS) { /* Configure output options and let the other side know * which ones we don't like. */ - if (rfc.mode == L2CAP_MODE_BASIC) { - if (mtu < pi->omtu) - result = L2CAP_CONF_UNACCEPT; - else { - pi->omtu = mtu; - pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; - } + if (mtu < L2CAP_DEFAULT_MIN_MTU) + result = L2CAP_CONF_UNACCEPT; + else { + pi->omtu = mtu; + pi->conf_state |= L2CAP_CONF_MTU_DONE; + } + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); - } else { + switch (rfc.mode) { + case L2CAP_MODE_BASIC: + pi->fcs = L2CAP_FCS_NONE; + pi->conf_state |= L2CAP_CONF_MODE_DONE; + break; + + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->remote_max_tx = rfc.max_transmit; + pi->max_pdu_size = rfc.max_pdu_size; + + rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; + rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + + pi->conf_state |= L2CAP_CONF_MODE_DONE; + break; + + case L2CAP_MODE_STREAMING: + pi->remote_tx_win = rfc.txwin_size; + pi->max_pdu_size = rfc.max_pdu_size; + + pi->conf_state |= L2CAP_CONF_MODE_DONE; + break; + + default: result = L2CAP_CONF_UNACCEPT; memset(&rfc, 0, sizeof(rfc)); - rfc.mode = L2CAP_MODE_BASIC; + rfc.mode = pi->mode; + } - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); - } - } + if (result == L2CAP_CONF_SUCCESS) + pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; + } rsp->scid = cpu_to_le16(pi->dcid); rsp->result = cpu_to_le16(result); rsp->flags = cpu_to_le16(0x0000); @@ -1856,6 +1973,73 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) return ptr - data; } +static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_conf_req *req = data; + void *ptr = req->data; + int type, olen; + unsigned long val; + struct l2cap_conf_rfc rfc; + + BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data); + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); + + switch (type) { + case L2CAP_CONF_MTU: + if (val < L2CAP_DEFAULT_MIN_MTU) { + *result = L2CAP_CONF_UNACCEPT; + pi->omtu = L2CAP_DEFAULT_MIN_MTU; + } else + pi->omtu = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + break; + + case L2CAP_CONF_FLUSH_TO: + pi->flush_to = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, + 2, pi->flush_to); + break; + + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *)val, olen); + + if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) && + rfc.mode != pi->mode) + return -ECONNREFUSED; + + pi->mode = rfc.mode; + pi->fcs = 0; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + break; + } + } + + if (*result == L2CAP_CONF_SUCCESS) { + switch (rfc.mode) { + case L2CAP_MODE_ERTM: + pi->remote_tx_win = rfc.txwin_size; + pi->retrans_timeout = rfc.retrans_timeout; + pi->monitor_timeout = rfc.monitor_timeout; + pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size); + break; + case L2CAP_MODE_STREAMING: + pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size); + break; + } + } + + req->dcid = cpu_to_le16(pi->dcid); + req->flags = cpu_to_le16(0x0000); + + return ptr - data; +} + static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags) { struct l2cap_conf_rsp *rsp = data; @@ -2042,6 +2226,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); + l2cap_pi(sk)->num_conf_req++; break; case L2CAP_CR_PEND: @@ -2100,10 +2285,17 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Complete config. */ len = l2cap_parse_conf_req(sk, rsp); - if (len < 0) + if (len < 0) { + struct l2cap_disconn_req req; + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_DISCONN_REQ, sizeof(req), &req); goto unlock; + } l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); + l2cap_pi(sk)->num_conf_rsp++; /* Reset config buffer. */ l2cap_pi(sk)->conf_len = 0; @@ -2121,6 +2313,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr u8 buf[64]; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; } unlock: @@ -2150,16 +2343,29 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr break; case L2CAP_CONF_UNACCEPT: - if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) { - char req[128]; - /* It does not make sense to adjust L2CAP parameters - * that are currently defined in the spec. We simply - * resend config request that we sent earlier. It is - * stupid, but it helps qualification testing which - * expects at least some response from us. */ - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, req), req); - goto done; + if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { + int len = cmd->len - sizeof(*rsp); + char req[64]; + + /* throw out any old stored conf requests */ + result = L2CAP_CONF_SUCCESS; + len = l2cap_parse_conf_rsp(sk, rsp->data, + len, req, &result); + if (len < 0) { + struct l2cap_disconn_req req; + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_DISCONN_REQ, sizeof(req), &req); + goto done; + } + + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_CONF_REQ, len, req); + l2cap_pi(sk)->num_conf_req++; + if (result != L2CAP_CONF_SUCCESS) + goto done; + break; } default: -- cgit v1.1 From 22121fc9152ca8f25a2d790860832ccb6a414c4d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 23 Jul 2009 10:27:23 -0300 Subject: Bluetooth: Create separate l2cap_send_disconn_req() function The code for sending a disconnect request was repeated several times within L2CAP source code. So move this into its own function. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index af0fbf9..c1b5620 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -366,6 +366,16 @@ static void l2cap_do_start(struct sock *sk) } } +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) +{ + struct l2cap_disconn_req req; + + req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_DISCONN_REQ, sizeof(req), &req); +} + /* ---- L2CAP connections ---- */ static void l2cap_conn_start(struct l2cap_conn *conn) { @@ -650,15 +660,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason) case BT_CONFIG: if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct l2cap_disconn_req req; sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - - req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); + l2cap_send_disconn_req(conn, sk); } else l2cap_chan_del(sk, reason); break; @@ -1786,13 +1791,8 @@ static int l2cap_build_conf_req(struct sock *sk, void *data) case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; - if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) { - struct l2cap_disconn_req req; - req.dcid = cpu_to_le16(pi->dcid); - req.scid = cpu_to_le16(pi->scid); - l2cap_send_cmd(pi->conn, l2cap_get_ident(pi->conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); - } + if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask)) + l2cap_send_disconn_req(pi->conn, sk); break; default: pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); @@ -2286,11 +2286,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Complete config. */ len = l2cap_parse_conf_req(sk, rsp); if (len < 0) { - struct l2cap_disconn_req req; - req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); + l2cap_send_disconn_req(conn, sk); goto unlock; } @@ -2352,11 +2348,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr len = l2cap_parse_conf_rsp(sk, rsp->data, len, req, &result); if (len < 0) { - struct l2cap_disconn_req req; - req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); + l2cap_send_disconn_req(conn, sk); goto done; } @@ -2372,13 +2364,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr sk->sk_state = BT_DISCONN; sk->sk_err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); - { - struct l2cap_disconn_req req; - req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); - } + l2cap_send_disconn_req(conn, sk); goto done; } -- cgit v1.1 From 1c2acffb76d4bc5fd27c4ea55cc27ad8ead10f9a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:25:57 -0300 Subject: Bluetooth: Add initial support for ERTM packets transfers This patch adds support for ERTM transfers, without retransmission, with txWindow up to 63 and with acknowledgement of packets received. Now the packets are queued before call l2cap_do_send(), so packets couldn't be sent at the time we call l2cap_sock_sendmsg(). They will be sent in an asynchronous way on later calls of l2cap_ertm_send(). Besides if an error occurs on calling l2cap_do_send() we disconnect the channel. Initially based on a patch from Nathan Holstein Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 384 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 335 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c1b5620..45b8697 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -333,6 +333,30 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 return hci_send_acl(conn->hcon, skb, 0); } +static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) +{ + struct sk_buff *skb; + struct l2cap_hdr *lh; + struct l2cap_conn *conn = pi->conn; + int count; + + BT_DBG("pi %p, control 0x%2.2x", pi, control); + + count = min_t(unsigned int, conn->mtu, L2CAP_HDR_SIZE + 2); + control |= L2CAP_CTRL_FRAME_TYPE; + + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(2); + lh->cid = cpu_to_le16(pi->dcid); + put_unaligned_le16(control, skb_put(skb, 2)); + + return hci_send_acl(pi->conn->hcon, skb, 0); +} + static void l2cap_do_start(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; @@ -1154,39 +1178,80 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } -static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len) +static void l2cap_drop_acked_frames(struct sock *sk) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sk_buff *skb, **frag; - int err, hlen, count, sent = 0; - struct l2cap_hdr *lh; + struct sk_buff *skb; - BT_DBG("sk %p len %d", sk, len); + while ((skb = skb_peek(TX_QUEUE(sk)))) { + if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) + break; - /* First fragment (with L2CAP header) */ - if (sk->sk_type == SOCK_DGRAM) - hlen = L2CAP_HDR_SIZE + 2; - else - hlen = L2CAP_HDR_SIZE; + skb = skb_dequeue(TX_QUEUE(sk)); + kfree_skb(skb); - count = min_t(unsigned int, (conn->mtu - hlen), len); + l2cap_pi(sk)->unacked_frames--; + } - skb = bt_skb_send_alloc(sk, hlen + count, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return err; + return; +} - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); +static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int err; + + BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len); + + err = hci_send_acl(pi->conn->hcon, skb, 0); + if (err < 0) + kfree_skb(skb); + + return err; +} + +static int l2cap_ertm_send(struct sock *sk) +{ + struct sk_buff *skb, *tx_skb; + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control; + int err; + + while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { + tx_skb = skb_clone(skb, GFP_ATOMIC); - if (sk->sk_type == SOCK_DGRAM) - put_unaligned(l2cap_pi(sk)->psm, (__le16 *) skb_put(skb, 2)); + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + err = l2cap_do_send(sk, tx_skb); + if (err < 0) { + l2cap_send_disconn_req(pi->conn, sk); + return err; + } + + bt_cb(skb)->tx_seq = pi->next_tx_seq; + pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + + pi->unacked_frames++; + + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + sk->sk_send_head = NULL; + else + sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + } + + return 0; +} + +static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff **frag; + int err, sent = 0; if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { - err = -EFAULT; - goto fail; + return -EFAULT; } sent += count; @@ -1199,33 +1264,112 @@ static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len) *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) - goto fail; - - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { - err = -EFAULT; - goto fail; - } + return -EFAULT; + if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) + return -EFAULT; sent += count; len -= count; frag = &(*frag)->next; } - err = hci_send_acl(conn->hcon, skb, 0); - if (err < 0) - goto fail; return sent; +} -fail: - kfree_skb(skb); - return err; +static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(-ENOMEM); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(control, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; } static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; - int err = 0; + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + u16 control; + int err; BT_DBG("sock %p, sk %p", sock, sk); @@ -1237,16 +1381,67 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return -EOPNOTSUPP; /* Check outgoing MTU */ - if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu) + if (sk->sk_type == SOCK_SEQPACKET && pi->mode == L2CAP_MODE_BASIC + && len > pi->omtu) return -EINVAL; lock_sock(sk); - if (sk->sk_state == BT_CONNECTED) - err = l2cap_do_send(sk, msg, len); - else + if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; + goto done; + } + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(sk, msg, len); + err = l2cap_do_send(sk, skb); + goto done; + } + switch (pi->mode) { + case L2CAP_MODE_BASIC: + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + + err = l2cap_do_send(sk, skb); + if (!err) + err = len; + break; + + case L2CAP_MODE_ERTM: + /* Entire SDU fits into one PDU */ + if (len <= pi->omtu) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_ertm_pdu(sk, msg, len, control); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + } else { + /* FIXME: Segmentation will be added later */ + err = -EINVAL; + goto done; + } + __skb_queue_tail(TX_QUEUE(sk), skb); + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; + + err = l2cap_ertm_send(sk); + if (!err) + err = len; + break; + + default: + BT_DBG("bad state %1.1x", pi->mode); + err = -EINVAL; + } + +done: release_sock(sk); return err; } @@ -2301,6 +2496,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { sk->sk_state = BT_CONNECTED; + l2cap_pi(sk)->next_tx_seq = 0; + l2cap_pi(sk)->expected_ack_seq = 0; + l2cap_pi(sk)->unacked_frames = 0; + __skb_queue_head_init(TX_QUEUE(sk)); l2cap_chan_ready(sk); goto unlock; } @@ -2375,6 +2574,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { sk->sk_state = BT_CONNECTED; + l2cap_pi(sk)->expected_tx_seq = 0; + l2cap_pi(sk)->num_to_ack = 0; + __skb_queue_head_init(TX_QUEUE(sk)); l2cap_chan_ready(sk); } @@ -2405,6 +2607,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd sk->sk_shutdown = SHUTDOWN_MASK; + skb_queue_purge(TX_QUEUE(sk)); + l2cap_chan_del(sk, ECONNRESET); bh_unlock_sock(sk); @@ -2427,6 +2631,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd if (!sk) return 0; + skb_queue_purge(TX_QUEUE(sk)); + l2cap_chan_del(sk, 0); bh_unlock_sock(sk); @@ -2602,9 +2808,60 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk kfree_skb(skb); } +static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_txseq(rx_control); + u16 tx_control = 0; + int err = 0; + + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + + if (tx_seq != pi->expected_tx_seq) + return -EINVAL; + + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + err = sock_queue_rcv_skb(sk, skb); + if (err) + return err; + + pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; + if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { + tx_control |= L2CAP_CTRL_FRAME_TYPE; + tx_control |= L2CAP_SUPER_RCV_READY; + tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + err = l2cap_send_sframe(pi, tx_control); + } + return err; +} + +static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + + BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + + switch (rx_control & L2CAP_CTRL_SUPERVISE) { + case L2CAP_SUPER_RCV_READY: + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); + l2cap_ertm_send(sk); + break; + + case L2CAP_SUPER_RCV_NOT_READY: + case L2CAP_SUPER_REJECT: + case L2CAP_SUPER_SELECT_REJECT: + break; + } + + return 0; +} + static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct sock *sk; + u16 control; + int err; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); if (!sk) { @@ -2617,16 +2874,40 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (sk->sk_state != BT_CONNECTED) goto drop; - if (l2cap_pi(sk)->imtu < skb->len) - goto drop; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + /* If socket recv buffers overflows we drop data here + * which is *bad* because L2CAP has to be reliable. + * But we don't have any other choice. L2CAP doesn't + * provide flow control mechanism. */ - /* If socket recv buffers overflows we drop data here - * which is *bad* because L2CAP has to be reliable. - * But we don't have any other choice. L2CAP doesn't - * provide flow control mechanism. */ + if (l2cap_pi(sk)->imtu < skb->len) + goto drop; - if (!sock_queue_rcv_skb(sk, skb)) - goto done; + if (!sock_queue_rcv_skb(sk, skb)) + goto done; + break; + + case L2CAP_MODE_ERTM: + control = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + + if (l2cap_pi(sk)->imtu < skb->len) + goto drop; + + if (__is_iframe(control)) + err = l2cap_data_channel_iframe(sk, control, skb); + else + err = l2cap_data_channel_sframe(sk, control, skb); + + if (!err) + goto done; + break; + + default: + BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode); + break; + } drop: kfree_skb(skb); @@ -2676,6 +2957,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) cid = __le16_to_cpu(lh->cid); len = __le16_to_cpu(lh->len); + if (len != skb->len) { + kfree_skb(skb); + return; + } + BT_DBG("len %d, cid 0x%4.4x", len, cid); switch (cid) { -- cgit v1.1 From c74e560cd0101455f1889515e1527e4c2e266113 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:25:58 -0300 Subject: Bluetooth: Add support for Segmentation and Reassembly of SDUs ERTM should use Segmentation and Reassembly to break down a SDU in many PDUs on sending data to the other side. On sending packets we queue all 'segments' until end of segmentation and just the add them to the queue for sending. On receiving we create a new SKB with the SDU reassembled. Initially based on a patch from Nathan Holstein Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 170 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 154 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 45b8697..167e025 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1334,7 +1334,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *ms return skb; } -static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control) +static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; @@ -1343,6 +1343,9 @@ static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg BT_DBG("sk %p len %d", sk, (int)len); + if (sdulen) + hlen += 2; + count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, msg->msg_flags & MSG_DONTWAIT, &err); @@ -1354,6 +1357,8 @@ static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); put_unaligned_le16(control, skb_put(skb, 2)); + if (sdulen) + put_unaligned_le16(sdulen, skb_put(skb, 2)); err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); if (unlikely(err < 0)) { @@ -1363,6 +1368,54 @@ static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg return skb; } +static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + struct sk_buff_head sar_queue; + u16 control; + size_t size = 0; + + __skb_queue_head_init(&sar_queue); + control = L2CAP_SDU_START; + skb = l2cap_create_ertm_pdu(sk, msg, pi->max_pdu_size, control, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + __skb_queue_tail(&sar_queue, skb); + len -= pi->max_pdu_size; + size +=pi->max_pdu_size; + control = 0; + + while (len > 0) { + size_t buflen; + + if (len > pi->max_pdu_size) { + control |= L2CAP_SDU_CONTINUE; + buflen = pi->max_pdu_size; + } else { + control |= L2CAP_SDU_END; + buflen = len; + } + + skb = l2cap_create_ertm_pdu(sk, msg, buflen, control, 0); + if (IS_ERR(skb)) { + skb_queue_purge(&sar_queue); + return PTR_ERR(skb); + } + + __skb_queue_tail(&sar_queue, skb); + len -= buflen; + size += buflen; + control = 0; + } + skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); + if (sk->sk_send_head == NULL) + sk->sk_send_head = sar_queue.next; + + return size; +} + static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; @@ -1415,21 +1468,22 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms case L2CAP_MODE_ERTM: /* Entire SDU fits into one PDU */ - if (len <= pi->omtu) { + if (len <= pi->max_pdu_size) { control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_ertm_pdu(sk, msg, len, control); + skb = l2cap_create_ertm_pdu(sk, msg, len, control, 0); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto done; } + __skb_queue_tail(TX_QUEUE(sk), skb); + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; } else { - /* FIXME: Segmentation will be added later */ - err = -EINVAL; - goto done; + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(sk, msg, len); + if (err < 0) + goto done; } - __skb_queue_tail(TX_QUEUE(sk), skb); - if (sk->sk_send_head == NULL) - sk->sk_send_head = skb; err = l2cap_ertm_send(sk); if (!err) @@ -2007,7 +2061,7 @@ done: rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU); + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2019,7 +2073,7 @@ done: rfc.max_transmit = 0; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU); + rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2808,6 +2862,86 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk kfree_skb(skb); } +static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *_skb; + int err = -EINVAL; + + switch (control & L2CAP_CTRL_SAR) { + case L2CAP_SDU_UNSEGMENTED: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(pi->sdu); + break; + } + + err = sock_queue_rcv_skb(sk, skb); + if (!err) + return 0; + + break; + + case L2CAP_SDU_START: + if (pi->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(pi->sdu); + break; + } + + pi->sdu_len = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + + pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); + if (!pi->sdu) { + err = -ENOMEM; + break; + } + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state |= L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len = skb->len; + err = 0; + break; + + case L2CAP_SDU_CONTINUE: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + break; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->partial_sdu_len += skb->len; + if (pi->partial_sdu_len > pi->sdu_len) + kfree_skb(pi->sdu); + else + err = 0; + + break; + + case L2CAP_SDU_END: + if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + break; + + memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + + pi->conn_state &= ~L2CAP_CONN_SAR_SDU; + pi->partial_sdu_len += skb->len; + + if (pi->partial_sdu_len == pi->sdu_len) { + _skb = skb_clone(pi->sdu, GFP_ATOMIC); + err = sock_queue_rcv_skb(sk, _skb); + if (err < 0) + kfree_skb(_skb); + } + kfree_skb(pi->sdu); + err = 0; + + break; + } + + kfree_skb(skb); + return err; +} + static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -2820,11 +2954,11 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str if (tx_seq != pi->expected_tx_seq) return -EINVAL; - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; - err = sock_queue_rcv_skb(sk, skb); - if (err) + err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); + if (err < 0) return err; + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { tx_control |= L2CAP_CTRL_FRAME_TYPE; @@ -2860,7 +2994,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct sock *sk; - u16 control; + u16 control, len; int err; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); @@ -2891,8 +3025,12 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk case L2CAP_MODE_ERTM: control = get_unaligned_le16(skb->data); skb_pull(skb, 2); + len = skb->len; - if (l2cap_pi(sk)->imtu < skb->len) + if (__is_sar_start(control)) + len -= 2; + + if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) goto drop; if (__is_iframe(control)) -- cgit v1.1 From 30afb5b2aa83adf4f69e5090d48e1bb04b64c58a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:25:59 -0300 Subject: Bluetooth: Initial support for retransmission of packets with REJ frames When receiving an I-frame with unexpected txSeq, receiver side start the recovery procedure by sending a REJ S-frame to the transmitter side. So the transmitter can re-send the lost I-frame. This patch just adds a basic support for retransmission, it doesn't mean that ERTM now has full support for packet retransmission. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 57 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 167e025..35e9f5b 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2951,22 +2951,36 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); - if (tx_seq != pi->expected_tx_seq) - return -EINVAL; + if (tx_seq == pi->expected_tx_seq) { + if (pi->conn_state & L2CAP_CONN_UNDER_REJ) + pi->conn_state &= ~L2CAP_CONN_UNDER_REJ; - err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); - if (err < 0) - return err; + err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); + if (err < 0) + return err; + + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; + if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { + tx_control |= L2CAP_SUPER_RCV_READY; + tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + goto send; + } + } else { + /* Unexpected txSeq. Send a REJ S-frame */ + kfree_skb(skb); + if (!(pi->conn_state & L2CAP_CONN_UNDER_REJ)) { + tx_control |= L2CAP_SUPER_REJECT; + tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + pi->conn_state |= L2CAP_CONN_UNDER_REJ; - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; - pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; - if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { - tx_control |= L2CAP_CTRL_FRAME_TYPE; - tx_control |= L2CAP_SUPER_RCV_READY; - tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - err = l2cap_send_sframe(pi, tx_control); + goto send; + } } - return err; + return 0; + +send: + return l2cap_send_sframe(pi, tx_control); } static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) @@ -2982,8 +2996,18 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str l2cap_ertm_send(sk); break; - case L2CAP_SUPER_RCV_NOT_READY: case L2CAP_SUPER_REJECT: + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); + + sk->sk_send_head = TX_QUEUE(sk)->next; + pi->next_tx_seq = pi->expected_ack_seq; + + l2cap_ertm_send(sk); + + break; + + case L2CAP_SUPER_RCV_NOT_READY: case L2CAP_SUPER_SELECT_REJECT: break; } @@ -3030,6 +3054,11 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (__is_sar_start(control)) len -= 2; + /* + * We can just drop the corrupted I-frame here. + * Receiver will miss it and start proper recovery + * procedures and ask retransmission. + */ if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) goto drop; -- cgit v1.1 From e90bac061b17cd81bd0df30606c64f4543bf5ca0 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:26:00 -0300 Subject: Bluetooth: Add support for Retransmission and Monitor Timers L2CAP uses retransmission and monitor timers to inquiry the other side about unacked I-frames. After sending each I-frame we (re)start the retransmission timer. If it expires, we start a monitor timer that send a S-frame with P bit set and wait for S-frame with F bit set. If monitor timer expires, try again, at a maximum of L2CAP_DEFAULT_MAX_TX. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 35e9f5b..97172f7 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1178,6 +1178,39 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } +static void l2cap_monitor_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + u16 control; + + if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { + l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk); + return; + } + + l2cap_pi(sk)->retry_count++; + __mod_monitor_timer(); + + control = L2CAP_CTRL_POLL; + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(l2cap_pi(sk), control); +} + +static void l2cap_retrans_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + u16 control; + + l2cap_pi(sk)->retry_count = 1; + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + + control = L2CAP_CTRL_POLL; + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(l2cap_pi(sk), control); +} + static void l2cap_drop_acked_frames(struct sock *sk) { struct sk_buff *skb; @@ -1192,6 +1225,9 @@ static void l2cap_drop_acked_frames(struct sock *sk) l2cap_pi(sk)->unacked_frames--; } + if (!l2cap_pi(sk)->unacked_frames) + del_timer(&l2cap_pi(sk)->retrans_timer); + return; } @@ -1216,19 +1252,32 @@ static int l2cap_ertm_send(struct sock *sk) u16 control; int err; + if (pi->conn_state & L2CAP_CONN_WAIT_F) + return 0; + while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { tx_skb = skb_clone(skb, GFP_ATOMIC); + if (pi->remote_max_tx && + bt_cb(skb)->retries == pi->remote_max_tx) { + l2cap_send_disconn_req(pi->conn, sk); + break; + } + + bt_cb(skb)->retries++; + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + err = l2cap_do_send(sk, tx_skb); if (err < 0) { l2cap_send_disconn_req(pi->conn, sk); return err; } + __mod_retrans_timer(); bt_cb(skb)->tx_seq = pi->next_tx_seq; pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; @@ -1365,6 +1414,8 @@ static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg kfree_skb(skb); return ERR_PTR(err); } + + bt_cb(skb)->retries = 0; return skb; } @@ -2058,7 +2109,7 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; - rfc.max_transmit = L2CAP_DEFAULT_MAX_RECEIVE; + rfc.max_transmit = L2CAP_DEFAULT_MAX_TX; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); @@ -2553,6 +2604,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr l2cap_pi(sk)->next_tx_seq = 0; l2cap_pi(sk)->expected_ack_seq = 0; l2cap_pi(sk)->unacked_frames = 0; + + setup_timer(&l2cap_pi(sk)->retrans_timer, + l2cap_retrans_timeout, (unsigned long) sk); + setup_timer(&l2cap_pi(sk)->monitor_timer, + l2cap_monitor_timeout, (unsigned long) sk); + __skb_queue_head_init(TX_QUEUE(sk)); l2cap_chan_ready(sk); goto unlock; @@ -2662,6 +2719,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd sk->sk_shutdown = SHUTDOWN_MASK; skb_queue_purge(TX_QUEUE(sk)); + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); l2cap_chan_del(sk, ECONNRESET); bh_unlock_sock(sk); @@ -2686,6 +2745,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd return 0; skb_queue_purge(TX_QUEUE(sk)); + del_timer(&l2cap_pi(sk)->retrans_timer); + del_timer(&l2cap_pi(sk)->monitor_timer); l2cap_chan_del(sk, 0); bh_unlock_sock(sk); @@ -2991,9 +3052,26 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str switch (rx_control & L2CAP_CTRL_SUPERVISE) { case L2CAP_SUPER_RCV_READY: - pi->expected_ack_seq = __get_reqseq(rx_control); - l2cap_drop_acked_frames(sk); - l2cap_ertm_send(sk); + if (rx_control & L2CAP_CTRL_POLL) { + u16 control = L2CAP_CTRL_FINAL; + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(l2cap_pi(sk), control); + } else if (rx_control & L2CAP_CTRL_FINAL) { + if (!(pi->conn_state & L2CAP_CONN_WAIT_F)) + break; + + pi->conn_state &= ~L2CAP_CONN_WAIT_F; + del_timer(&pi->monitor_timer); + + if (pi->unacked_frames > 0) + __mod_retrans_timer(); + } else { + pi->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(sk); + if (pi->unacked_frames > 0) + __mod_retrans_timer(); + l2cap_ertm_send(sk); + } break; case L2CAP_SUPER_REJECT: -- cgit v1.1 From 6840ed0770d79b9bb0800e5e026a067040ef18f5 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:26:01 -0300 Subject: Bluetooth: Enable Streaming Mode for L2CAP Streaming Mode is helpful for the Bluetooth streaming based profiles, such as A2DP. It doesn't have any error control or flow control. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 82 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 97172f7..7f835e7 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1245,6 +1245,39 @@ static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb) return err; } +static int l2cap_streaming_send(struct sock *sk) +{ + struct sk_buff *skb, *tx_skb; + struct l2cap_pinfo *pi = l2cap_pi(sk); + u16 control; + int err; + + while ((skb = sk->sk_send_head)) { + tx_skb = skb_clone(skb, GFP_ATOMIC); + + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + err = l2cap_do_send(sk, tx_skb); + if (err < 0) { + l2cap_send_disconn_req(pi->conn, sk); + return err; + } + + pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + sk->sk_send_head = NULL; + else + sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + + skb = skb_dequeue(TX_QUEUE(sk)); + kfree_skb(skb); + } + return 0; +} + static int l2cap_ertm_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; @@ -1383,7 +1416,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *ms return skb; } -static struct sk_buff *l2cap_create_ertm_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; @@ -1429,7 +1462,7 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz __skb_queue_head_init(&sar_queue); control = L2CAP_SDU_START; - skb = l2cap_create_ertm_pdu(sk, msg, pi->max_pdu_size, control, len); + skb = l2cap_create_iframe_pdu(sk, msg, pi->max_pdu_size, control, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1449,7 +1482,7 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz buflen = len; } - skb = l2cap_create_ertm_pdu(sk, msg, buflen, control, 0); + skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0); if (IS_ERR(skb)) { skb_queue_purge(&sar_queue); return PTR_ERR(skb); @@ -1518,10 +1551,11 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms break; case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: /* Entire SDU fits into one PDU */ if (len <= pi->max_pdu_size) { control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_ertm_pdu(sk, msg, len, control, 0); + skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto done; @@ -1536,7 +1570,11 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms goto done; } - err = l2cap_ertm_send(sk); + if (pi->mode == L2CAP_MODE_STREAMING) + err = l2cap_streaming_send(sk); + else + err = l2cap_ertm_send(sk); + if (!err) err = len; break; @@ -2050,7 +2088,7 @@ static int l2cap_mode_supported(__u8 mode, __u32 feat_mask) { u32 local_feat_mask = l2cap_feat_mask; if (enable_ertm) - local_feat_mask |= L2CAP_FEAT_ERTM; + local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; switch (mode) { case L2CAP_MODE_ERTM: @@ -2771,7 +2809,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); if (enable_ertm) - feat_mask |= L2CAP_FEAT_ERTM; + feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); @@ -3096,7 +3134,9 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { struct sock *sk; + struct l2cap_pinfo *pi; u16 control, len; + u8 tx_seq; int err; sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); @@ -3105,19 +3145,21 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto drop; } + pi = l2cap_pi(sk); + BT_DBG("sk %p, len %d", sk, skb->len); if (sk->sk_state != BT_CONNECTED) goto drop; - switch (l2cap_pi(sk)->mode) { + switch (pi->mode) { case L2CAP_MODE_BASIC: /* If socket recv buffers overflows we drop data here * which is *bad* because L2CAP has to be reliable. * But we don't have any other choice. L2CAP doesn't * provide flow control mechanism. */ - if (l2cap_pi(sk)->imtu < skb->len) + if (pi->imtu < skb->len) goto drop; if (!sock_queue_rcv_skb(sk, skb)) @@ -3149,6 +3191,28 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto done; break; + case L2CAP_MODE_STREAMING: + control = get_unaligned_le16(skb->data); + skb_pull(skb, 2); + len = skb->len; + + if (__is_sar_start(control)) + len -= 2; + + if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control)) + goto drop; + + tx_seq = __get_txseq(control); + + if (pi->expected_tx_seq == tx_seq) + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + else + pi->expected_tx_seq = tx_seq + 1; + + err = l2cap_sar_reassembly_sdu(sk, skb, control); + + goto done; + default: BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode); break; -- cgit v1.1 From fcc203c30d72dde82692f6b761a80e5ca5fdd8fa Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:26:02 -0300 Subject: Bluetooth: Add support for FCS option to L2CAP Implement CRC16 check for L2CAP packets. FCS is used by Streaming Mode and Enhanced Retransmission Mode and is a extra check for the packet content. Using CRC16 is the default, L2CAP won't use FCS only when both side send a "No FCS" request. Initially based on a patch from Nathan Holstein Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 7f835e7..4c31900 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -338,11 +339,14 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) struct sk_buff *skb; struct l2cap_hdr *lh; struct l2cap_conn *conn = pi->conn; - int count; + int count, hlen = L2CAP_HDR_SIZE + 2; + + if (pi->fcs == L2CAP_FCS_CRC16) + hlen += 2; BT_DBG("pi %p, control 0x%2.2x", pi, control); - count = min_t(unsigned int, conn->mtu, L2CAP_HDR_SIZE + 2); + count = min_t(unsigned int, conn->mtu, hlen); control |= L2CAP_CTRL_FRAME_TYPE; skb = bt_skb_alloc(count, GFP_ATOMIC); @@ -350,10 +354,15 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) return -ENOMEM; lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(2); + lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(pi->dcid); put_unaligned_le16(control, skb_put(skb, 2)); + if (pi->fcs == L2CAP_FCS_CRC16) { + u16 fcs = crc16(0, (u8 *)lh, count - 2); + put_unaligned_le16(fcs, skb_put(skb, 2)); + } + return hci_send_acl(pi->conn->hcon, skb, 0); } @@ -1249,7 +1258,7 @@ static int l2cap_streaming_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); - u16 control; + u16 control, fcs; int err; while ((skb = sk->sk_send_head)) { @@ -1259,6 +1268,11 @@ static int l2cap_streaming_send(struct sock *sk) control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + } + err = l2cap_do_send(sk, tx_skb); if (err < 0) { l2cap_send_disconn_req(pi->conn, sk); @@ -1282,7 +1296,7 @@ static int l2cap_ertm_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); - u16 control; + u16 control, fcs; int err; if (pi->conn_state & L2CAP_CONN_WAIT_F) @@ -1305,6 +1319,11 @@ static int l2cap_ertm_send(struct sock *sk) put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); + } + err = l2cap_do_send(sk, tx_skb); if (err < 0) { l2cap_send_disconn_req(pi->conn, sk); @@ -1428,6 +1447,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m if (sdulen) hlen += 2; + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + hlen += 2; + count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, msg->msg_flags & MSG_DONTWAIT, &err); @@ -1448,6 +1470,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m return ERR_PTR(err); } + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + put_unaligned_le16(0, skb_put(skb, 2)); + bt_cb(skb)->retries = 0; return skb; } @@ -1633,6 +1658,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -1643,6 +1669,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us l2cap_pi(sk)->imtu = opts.imtu; l2cap_pi(sk)->omtu = opts.omtu; l2cap_pi(sk)->mode = opts.mode; + l2cap_pi(sk)->fcs = opts.fcs; break; case L2CAP_LM: @@ -1756,6 +1783,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -2154,6 +2182,15 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + break; + + if (pi->fcs == L2CAP_FCS_NONE || + pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { + pi->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + } break; case L2CAP_MODE_STREAMING: @@ -2166,6 +2203,15 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + + if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + break; + + if (pi->fcs == L2CAP_FCS_NONE || + pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { + pi->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + } break; } @@ -2217,6 +2263,12 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) memcpy(&rfc, (void *) val, olen); break; + case L2CAP_CONF_FCS: + if (val == L2CAP_FCS_NONE) + pi->conf_state |= L2CAP_CONF_NO_FCS_RECV; + + break; + default: if (hint) break; @@ -2638,6 +2690,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr goto unlock; if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) + || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) + l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + sk->sk_state = BT_CONNECTED; l2cap_pi(sk)->next_tx_seq = 0; l2cap_pi(sk)->expected_ack_seq = 0; @@ -2722,6 +2778,10 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV) + || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE) + l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16; + sk->sk_state = BT_CONNECTED; l2cap_pi(sk)->expected_tx_seq = 0; l2cap_pi(sk)->num_to_ack = 0; @@ -2809,7 +2869,8 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); if (enable_ertm) - feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; + feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING + | L2CAP_FEAT_FCS; put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); @@ -2961,6 +3022,22 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk kfree_skb(skb); } +static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) +{ + u16 our_fcs, rcv_fcs; + int hdr_size = L2CAP_HDR_SIZE + 2; + + if (pi->fcs == L2CAP_FCS_CRC16) { + skb_trim(skb, skb->len - 2); + rcv_fcs = get_unaligned_le16(skb->data + skb->len); + our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); + + if (our_fcs != rcv_fcs) + return -EINVAL; + } + return 0; +} + static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) { struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -3174,6 +3251,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (__is_sar_start(control)) len -= 2; + if (pi->fcs == L2CAP_FCS_CRC16) + len -= 2; + /* * We can just drop the corrupted I-frame here. * Receiver will miss it and start proper recovery @@ -3182,6 +3262,9 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (len > L2CAP_DEFAULT_MAX_PDU_SIZE) goto drop; + if (l2cap_check_fcs(pi, skb)) + goto drop; + if (__is_iframe(control)) err = l2cap_data_channel_iframe(sk, control, skb); else @@ -3199,9 +3282,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (__is_sar_start(control)) len -= 2; + if (pi->fcs == L2CAP_FCS_CRC16) + len -= 2; + if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control)) goto drop; + if (l2cap_check_fcs(pi, skb)) + goto drop; + tx_seq = __get_txseq(control); if (pi->expected_tx_seq == tx_seq) -- cgit v1.1 From 8f17154f1f70fcc6faa31ac82164fcf7f0599f38 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:26:03 -0300 Subject: Bluetooth: Add support for L2CAP SREJ exception When L2CAP loses an I-frame we send a SREJ frame to the transmitter side requesting the lost packet. This patch implement all Recv I-frame events on SREJ_SENT state table except the ones that deal with SendRej (the REJ exception at receiver side is yet not implemented). Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 220 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 196 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 4c31900..70aff92 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1292,6 +1292,50 @@ static int l2cap_streaming_send(struct sock *sk) return 0; } +static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb, *tx_skb; + u16 control, fcs; + int err; + + skb = skb_peek(TX_QUEUE(sk)); + do { + if (bt_cb(skb)->tx_seq != tx_seq) { + if (skb_queue_is_last(TX_QUEUE(sk), skb)) + break; + skb = skb_queue_next(TX_QUEUE(sk), skb); + continue; + } + + if (pi->remote_max_tx && + bt_cb(skb)->retries == pi->remote_max_tx) { + l2cap_send_disconn_req(pi->conn, sk); + break; + } + + tx_skb = skb_clone(skb, GFP_ATOMIC); + bt_cb(skb)->retries++; + control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) { + fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); + put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + } + + err = l2cap_do_send(sk, tx_skb); + if (err < 0) { + l2cap_send_disconn_req(pi->conn, sk); + return err; + } + break; + } while(1); + return 0; +} + static int l2cap_ertm_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; @@ -2705,6 +2749,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr l2cap_monitor_timeout, (unsigned long) sk); __skb_queue_head_init(TX_QUEUE(sk)); + __skb_queue_head_init(SREJ_QUEUE(sk)); l2cap_chan_ready(sk); goto unlock; } @@ -2784,8 +2829,10 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr sk->sk_state = BT_CONNECTED; l2cap_pi(sk)->expected_tx_seq = 0; + l2cap_pi(sk)->buffer_seq = 0; l2cap_pi(sk)->num_to_ack = 0; __skb_queue_head_init(TX_QUEUE(sk)); + __skb_queue_head_init(SREJ_QUEUE(sk)); l2cap_chan_ready(sk); } @@ -2817,6 +2864,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd sk->sk_shutdown = SHUTDOWN_MASK; skb_queue_purge(TX_QUEUE(sk)); + skb_queue_purge(SREJ_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); @@ -2843,6 +2891,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd return 0; skb_queue_purge(TX_QUEUE(sk)); + skb_queue_purge(SREJ_QUEUE(sk)); del_timer(&l2cap_pi(sk)->retrans_timer); del_timer(&l2cap_pi(sk)->monitor_timer); @@ -3038,6 +3087,33 @@ static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) return 0; } +static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) +{ + struct sk_buff *next_skb; + + bt_cb(skb)->tx_seq = tx_seq; + bt_cb(skb)->sar = sar; + + next_skb = skb_peek(SREJ_QUEUE(sk)); + if (!next_skb) { + __skb_queue_tail(SREJ_QUEUE(sk), skb); + return; + } + + do { + if (bt_cb(next_skb)->tx_seq > tx_seq) { + __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb); + return; + } + + if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb)) + break; + + } while((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); + + __skb_queue_tail(SREJ_QUEUE(sk), skb); +} + static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) { struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -3118,50 +3194,143 @@ static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 co return err; } +static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) +{ + struct sk_buff *skb; + u16 control = 0; + + while((skb = skb_peek(SREJ_QUEUE(sk)))) { + if (bt_cb(skb)->tx_seq != tx_seq) + break; + + skb = skb_dequeue(SREJ_QUEUE(sk)); + control |= bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + l2cap_sar_reassembly_sdu(sk, skb, control); + l2cap_pi(sk)->buffer_seq_srej = + (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; + tx_seq++; + } +} + +static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct srej_list *l, *tmp; + u16 control; + + list_for_each_entry_safe(l,tmp, SREJ_LIST(sk), list) { + if (l->tx_seq == tx_seq) { + list_del(&l->list); + kfree(l); + return; + } + control = L2CAP_SUPER_SELECT_REJECT; + control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(pi, control); + list_del(&l->list); + list_add_tail(&l->list, SREJ_LIST(sk)); + } +} + +static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct srej_list *new; + u16 control; + + while (tx_seq != pi->expected_tx_seq) { + control = L2CAP_SUPER_SELECT_REJECT; + control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(pi, control); + + new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); + new->tx_seq = pi->expected_tx_seq++; + list_add_tail(&new->list, SREJ_LIST(sk)); + } + pi->expected_tx_seq++; +} + static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_txseq(rx_control); u16 tx_control = 0; + u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; int err = 0; BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); - if (tx_seq == pi->expected_tx_seq) { - if (pi->conn_state & L2CAP_CONN_UNDER_REJ) - pi->conn_state &= ~L2CAP_CONN_UNDER_REJ; + if (tx_seq == pi->expected_tx_seq) + goto expected; - err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); - if (err < 0) - return err; + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + struct srej_list *first; - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; - pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; - if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { - tx_control |= L2CAP_SUPER_RCV_READY; - tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - goto send; + first = list_first_entry(SREJ_LIST(sk), + struct srej_list, list); + if (tx_seq == first->tx_seq) { + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + l2cap_check_srej_gap(sk, tx_seq); + + list_del(&first->list); + kfree(first); + + if (list_empty(SREJ_LIST(sk))) { + pi->buffer_seq = pi->buffer_seq_srej; + pi->conn_state &= ~L2CAP_CONN_SREJ_SENT; + } + } else { + struct srej_list *l; + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + + list_for_each_entry(l, SREJ_LIST(sk), list) { + if (l->tx_seq == tx_seq) { + l2cap_resend_srejframe(sk, tx_seq); + return 0; + } + } + l2cap_send_srejframe(sk, tx_seq); } } else { - /* Unexpected txSeq. Send a REJ S-frame */ - kfree_skb(skb); - if (!(pi->conn_state & L2CAP_CONN_UNDER_REJ)) { - tx_control |= L2CAP_SUPER_REJECT; - tx_control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - pi->conn_state |= L2CAP_CONN_UNDER_REJ; + pi->conn_state |= L2CAP_CONN_SREJ_SENT; - goto send; - } + INIT_LIST_HEAD(SREJ_LIST(sk)); + pi->buffer_seq_srej = pi->buffer_seq; + + __skb_queue_head_init(SREJ_QUEUE(sk)); + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + + l2cap_send_srejframe(sk, tx_seq); } return 0; -send: - return l2cap_send_sframe(pi, tx_control); +expected: + pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + + if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + return 0; + } + + pi->buffer_seq = (pi->buffer_seq + 1) % 64; + + err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); + if (err < 0) + return err; + + pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK; + if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) { + tx_control |= L2CAP_SUPER_RCV_READY; + tx_control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(pi, tx_control); + } + return 0; } static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); + u8 tx_seq = __get_reqseq(rx_control); BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); @@ -3181,7 +3350,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str if (pi->unacked_frames > 0) __mod_retrans_timer(); } else { - pi->expected_ack_seq = __get_reqseq(rx_control); + pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); if (pi->unacked_frames > 0) __mod_retrans_timer(); @@ -3200,8 +3369,11 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str break; - case L2CAP_SUPER_RCV_NOT_READY: case L2CAP_SUPER_SELECT_REJECT: + l2cap_retransmit_frame(sk, tx_seq); + break; + + case L2CAP_SUPER_RCV_NOT_READY: break; } -- cgit v1.1 From ef54fd937fbd5ebaeb023818524565bd526a5f36 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Aug 2009 22:26:04 -0300 Subject: Bluetooth: Full support for receiving L2CAP SREJ frames Support for receiving of SREJ frames as specified by the state table. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 70aff92..c04526f 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3241,6 +3241,10 @@ static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) while (tx_seq != pi->expected_tx_seq) { control = L2CAP_SUPER_SELECT_REJECT; control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + if (pi->conn_state & L2CAP_CONN_SEND_PBIT) { + control |= L2CAP_CTRL_POLL; + pi->conn_state &= ~L2CAP_CONN_SEND_PBIT; + } l2cap_send_sframe(pi, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); @@ -3300,6 +3304,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str __skb_queue_head_init(SREJ_QUEUE(sk)); l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + pi->conn_state |= L2CAP_CONN_SEND_PBIT; + l2cap_send_srejframe(sk, tx_seq); } return 0; @@ -3370,7 +3376,29 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str break; case L2CAP_SUPER_SELECT_REJECT: - l2cap_retransmit_frame(sk, tx_seq); + if (rx_control & L2CAP_CTRL_POLL) { + l2cap_retransmit_frame(sk, tx_seq); + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + l2cap_ertm_send(sk); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; + } + } else if (rx_control & L2CAP_CTRL_FINAL) { + if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) && + pi->srej_save_reqseq == tx_seq) + pi->srej_save_reqseq &= ~L2CAP_CONN_SREJ_ACT; + else + l2cap_retransmit_frame(sk, tx_seq); + } + else { + l2cap_retransmit_frame(sk, tx_seq); + if (pi->conn_state & L2CAP_CONN_WAIT_F) { + pi->srej_save_reqseq = tx_seq; + pi->conn_state |= L2CAP_CONN_SREJ_ACT; + } + } break; case L2CAP_SUPER_RCV_NOT_READY: -- cgit v1.1 From 9e726b17422bade75fba94e625cd35fd1353e682 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jul 2009 13:50:58 -0300 Subject: Bluetooth: Fix rejected connection not disconnecting ACL link When using DEFER_SETUP on a RFCOMM socket, a SABM frame triggers authorization which when rejected send a DM response. This is fine according to the RFCOMM spec: the responding implementation may replace the "proper" response on the Multiplexer Control channel with a DM frame, sent on the referenced DLCI to indicate that the DLCI is not open, and that the responder would not grant a request to open it later either. But some stacks doesn't seems to cope with this leaving DLCI 0 open after receiving DM frame. To fix it properly a timer was introduced to rfcomm_session which is used to set a timeout when the last active DLC of a session is unlinked, this will give the remote stack some time to reply with a proper DISC frame on DLCI 0 avoiding both sides sending DISC to each other on stacks that follow the specification and taking care of those who don't by taking down DLCI 0. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'net') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 26af485..25692bc 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d) auth_type); } +static void rfcomm_session_timeout(unsigned long arg) +{ + struct rfcomm_session *s = (void *) arg; + + BT_DBG("session %p state %ld", s, s->state); + + set_bit(RFCOMM_TIMED_OUT, &s->flags); + rfcomm_session_put(s); + rfcomm_schedule(RFCOMM_SCHED_TIMEO); +} + +static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) +{ + BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); + + if (!mod_timer(&s->timer, jiffies + timeout)) + rfcomm_session_hold(s); +} + +static void rfcomm_session_clear_timer(struct rfcomm_session *s) +{ + BT_DBG("session %p state %ld", s, s->state); + + if (timer_pending(&s->timer) && del_timer(&s->timer)) + rfcomm_session_put(s); +} + /* ---- RFCOMM DLCs ---- */ static void rfcomm_dlc_timeout(unsigned long arg) { @@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) rfcomm_session_hold(s); + rfcomm_session_clear_timer(s); rfcomm_dlc_hold(d); list_add(&d->list, &s->dlcs); d->session = s; @@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) d->session = NULL; rfcomm_dlc_put(d); + if (list_empty(&s->dlcs)) + rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); + rfcomm_session_put(s); } @@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) BT_DBG("session %p sock %p", s, sock); + setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); + INIT_LIST_HEAD(&s->dlcs); s->state = state; s->sock = sock; @@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s) if (state == BT_CONNECTED) rfcomm_send_disc(s, 0); + rfcomm_session_clear_timer(s); sock_release(s->sock); kfree(s); @@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) __rfcomm_dlc_close(d, err); } + rfcomm_session_clear_timer(s); rfcomm_session_put(s); } @@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void) struct rfcomm_session *s; s = list_entry(p, struct rfcomm_session, list); + if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { + s->state = BT_DISCONN; + rfcomm_send_disc(s, 0); + continue; + } + if (s->state == BT_LISTEN) { rfcomm_accept_connection(s); continue; -- cgit v1.1 From 70041088e3b976627ba9a183b812f39ef8a9ba0e Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 21 Aug 2009 12:28:31 +0000 Subject: RDS: Add TCP transport to RDS This code allows RDS to be tunneled over a TCP connection. RDMA operations are disabled when using TCP transport, but this frees RDS from the IB/RDMA stack dependency, and allows it to be used with standard Ethernet adapters, or in a VM. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/tcp.c | 319 ++++++++++++++++++++++++++++++++++++++++++++ net/rds/tcp.h | 93 +++++++++++++ net/rds/tcp_connect.c | 153 ++++++++++++++++++++++ net/rds/tcp_listen.c | 199 ++++++++++++++++++++++++++++ net/rds/tcp_recv.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/rds/tcp_send.c | 263 +++++++++++++++++++++++++++++++++++++ net/rds/tcp_stats.c | 74 +++++++++++ 7 files changed, 1457 insertions(+) create mode 100644 net/rds/tcp.c create mode 100644 net/rds/tcp.h create mode 100644 net/rds/tcp_connect.c create mode 100644 net/rds/tcp_listen.c create mode 100644 net/rds/tcp_recv.c create mode 100644 net/rds/tcp_send.c create mode 100644 net/rds/tcp_stats.c (limited to 'net') diff --git a/net/rds/tcp.c b/net/rds/tcp.c new file mode 100644 index 0000000..e0ac900 --- /dev/null +++ b/net/rds/tcp.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include + +#include "rds.h" +#include "tcp.h" + +/* only for info exporting */ +static DEFINE_SPINLOCK(rds_tcp_tc_list_lock); +static LIST_HEAD(rds_tcp_tc_list); +unsigned int rds_tcp_tc_count; + +/* Track rds_tcp_connection structs so they can be cleaned up */ +static DEFINE_SPINLOCK(rds_tcp_conn_lock); +static LIST_HEAD(rds_tcp_conn_list); + +static struct kmem_cache *rds_tcp_conn_slab; + +#define RDS_TCP_DEFAULT_BUFSIZE (128 * 1024) + +/* doing it this way avoids calling tcp_sk() */ +void rds_tcp_nonagle(struct socket *sock) +{ + mm_segment_t oldfs = get_fs(); + int val = 1; + + set_fs(KERNEL_DS); + sock->ops->setsockopt(sock, SOL_TCP, TCP_NODELAY, (char __user *)&val, + sizeof(val)); + set_fs(oldfs); +} + +void rds_tcp_tune(struct socket *sock) +{ + struct sock *sk = sock->sk; + + rds_tcp_nonagle(sock); + + /* + * We're trying to saturate gigabit with the default, + * see svc_sock_setbufsize(). + */ + lock_sock(sk); + sk->sk_sndbuf = RDS_TCP_DEFAULT_BUFSIZE; + sk->sk_rcvbuf = RDS_TCP_DEFAULT_BUFSIZE; + sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; + release_sock(sk); +} + +u32 rds_tcp_snd_nxt(struct rds_tcp_connection *tc) +{ + return tcp_sk(tc->t_sock->sk)->snd_nxt; +} + +u32 rds_tcp_snd_una(struct rds_tcp_connection *tc) +{ + return tcp_sk(tc->t_sock->sk)->snd_una; +} + +void rds_tcp_restore_callbacks(struct socket *sock, + struct rds_tcp_connection *tc) +{ + rdsdebug("restoring sock %p callbacks from tc %p\n", sock, tc); + write_lock_bh(&sock->sk->sk_callback_lock); + + /* done under the callback_lock to serialize with write_space */ + spin_lock(&rds_tcp_tc_list_lock); + list_del_init(&tc->t_list_item); + rds_tcp_tc_count--; + spin_unlock(&rds_tcp_tc_list_lock); + + tc->t_sock = NULL; + + sock->sk->sk_write_space = tc->t_orig_write_space; + sock->sk->sk_data_ready = tc->t_orig_data_ready; + sock->sk->sk_state_change = tc->t_orig_state_change; + sock->sk->sk_user_data = NULL; + + write_unlock_bh(&sock->sk->sk_callback_lock); +} + +/* + * This is the only path that sets tc->t_sock. Send and receive trust that + * it is set. The RDS_CONN_CONNECTED bit protects those paths from being + * called while it isn't set. + */ +void rds_tcp_set_callbacks(struct socket *sock, struct rds_connection *conn) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + + rdsdebug("setting sock %p callbacks to tc %p\n", sock, tc); + write_lock_bh(&sock->sk->sk_callback_lock); + + /* done under the callback_lock to serialize with write_space */ + spin_lock(&rds_tcp_tc_list_lock); + list_add_tail(&tc->t_list_item, &rds_tcp_tc_list); + rds_tcp_tc_count++; + spin_unlock(&rds_tcp_tc_list_lock); + + /* accepted sockets need our listen data ready undone */ + if (sock->sk->sk_data_ready == rds_tcp_listen_data_ready) + sock->sk->sk_data_ready = sock->sk->sk_user_data; + + tc->t_sock = sock; + tc->conn = conn; + tc->t_orig_data_ready = sock->sk->sk_data_ready; + tc->t_orig_write_space = sock->sk->sk_write_space; + tc->t_orig_state_change = sock->sk->sk_state_change; + + sock->sk->sk_user_data = conn; + sock->sk->sk_data_ready = rds_tcp_data_ready; + sock->sk->sk_write_space = rds_tcp_write_space; + sock->sk->sk_state_change = rds_tcp_state_change; + + write_unlock_bh(&sock->sk->sk_callback_lock); +} + +static void rds_tcp_tc_info(struct socket *sock, unsigned int len, + struct rds_info_iterator *iter, + struct rds_info_lengths *lens) +{ + struct rds_info_tcp_socket tsinfo; + struct rds_tcp_connection *tc; + unsigned long flags; + struct sockaddr_in sin; + int sinlen; + + spin_lock_irqsave(&rds_tcp_tc_list_lock, flags); + + if (len / sizeof(tsinfo) < rds_tcp_tc_count) + goto out; + + list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { + + sock->ops->getname(sock, (struct sockaddr *)&sin, &sinlen, 0); + tsinfo.local_addr = sin.sin_addr.s_addr; + tsinfo.local_port = sin.sin_port; + sock->ops->getname(sock, (struct sockaddr *)&sin, &sinlen, 1); + tsinfo.peer_addr = sin.sin_addr.s_addr; + tsinfo.peer_port = sin.sin_port; + + tsinfo.hdr_rem = tc->t_tinc_hdr_rem; + tsinfo.data_rem = tc->t_tinc_data_rem; + tsinfo.last_sent_nxt = tc->t_last_sent_nxt; + tsinfo.last_expected_una = tc->t_last_expected_una; + tsinfo.last_seen_una = tc->t_last_seen_una; + + rds_info_copy(iter, &tsinfo, sizeof(tsinfo)); + } + +out: + lens->nr = rds_tcp_tc_count; + lens->each = sizeof(tsinfo); + + spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags); +} + +static int rds_tcp_laddr_check(__be32 addr) +{ + if (inet_addr_type(&init_net, addr) == RTN_LOCAL) + return 0; + return -EADDRNOTAVAIL; +} + +static int rds_tcp_conn_alloc(struct rds_connection *conn, gfp_t gfp) +{ + struct rds_tcp_connection *tc; + + tc = kmem_cache_alloc(rds_tcp_conn_slab, gfp); + if (tc == NULL) + return -ENOMEM; + + tc->t_sock = NULL; + tc->t_tinc = NULL; + tc->t_tinc_hdr_rem = sizeof(struct rds_header); + tc->t_tinc_data_rem = 0; + + conn->c_transport_data = tc; + + spin_lock_irq(&rds_tcp_conn_lock); + list_add_tail(&tc->t_tcp_node, &rds_tcp_conn_list); + spin_unlock_irq(&rds_tcp_conn_lock); + + rdsdebug("alloced tc %p\n", conn->c_transport_data); + return 0; +} + +static void rds_tcp_conn_free(void *arg) +{ + struct rds_tcp_connection *tc = arg; + rdsdebug("freeing tc %p\n", tc); + kmem_cache_free(rds_tcp_conn_slab, tc); +} + +static void rds_tcp_destroy_conns(void) +{ + struct rds_tcp_connection *tc, *_tc; + LIST_HEAD(tmp_list); + + /* avoid calling conn_destroy with irqs off */ + spin_lock_irq(&rds_tcp_conn_lock); + list_splice(&rds_tcp_conn_list, &tmp_list); + INIT_LIST_HEAD(&rds_tcp_conn_list); + spin_unlock_irq(&rds_tcp_conn_lock); + + list_for_each_entry_safe(tc, _tc, &tmp_list, t_tcp_node) { + if (tc->conn->c_passive) + rds_conn_destroy(tc->conn->c_passive); + rds_conn_destroy(tc->conn); + } +} + +void rds_tcp_exit(void) +{ + rds_info_deregister_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info); + rds_tcp_listen_stop(); + rds_tcp_destroy_conns(); + rds_trans_unregister(&rds_tcp_transport); + rds_tcp_recv_exit(); + kmem_cache_destroy(rds_tcp_conn_slab); +} +module_exit(rds_tcp_exit); + +struct rds_transport rds_tcp_transport = { + .laddr_check = rds_tcp_laddr_check, + .xmit_prepare = rds_tcp_xmit_prepare, + .xmit_complete = rds_tcp_xmit_complete, + .xmit_cong_map = rds_tcp_xmit_cong_map, + .xmit = rds_tcp_xmit, + .recv = rds_tcp_recv, + .conn_alloc = rds_tcp_conn_alloc, + .conn_free = rds_tcp_conn_free, + .conn_connect = rds_tcp_conn_connect, + .conn_shutdown = rds_tcp_conn_shutdown, + .inc_copy_to_user = rds_tcp_inc_copy_to_user, + .inc_purge = rds_tcp_inc_purge, + .inc_free = rds_tcp_inc_free, + .stats_info_copy = rds_tcp_stats_info_copy, + .exit = rds_tcp_exit, + .t_owner = THIS_MODULE, + .t_name = "tcp", + .t_prefer_loopback = 1, +}; + +int __init rds_tcp_init(void) +{ + int ret; + + rds_tcp_conn_slab = kmem_cache_create("rds_tcp_connection", + sizeof(struct rds_tcp_connection), + 0, 0, NULL); + if (rds_tcp_conn_slab == NULL) { + ret = -ENOMEM; + goto out; + } + + ret = rds_tcp_recv_init(); + if (ret) + goto out_slab; + + ret = rds_trans_register(&rds_tcp_transport); + if (ret) + goto out_recv; + + ret = rds_tcp_listen_init(); + if (ret) + goto out_register; + + rds_info_register_func(RDS_INFO_TCP_SOCKETS, rds_tcp_tc_info); + + goto out; + +out_register: + rds_trans_unregister(&rds_tcp_transport); +out_recv: + rds_tcp_recv_exit(); +out_slab: + kmem_cache_destroy(rds_tcp_conn_slab); +out: + return ret; +} +module_init(rds_tcp_init); + +MODULE_AUTHOR("Oracle Corporation "); +MODULE_DESCRIPTION("RDS: TCP transport"); +MODULE_LICENSE("Dual BSD/GPL"); + diff --git a/net/rds/tcp.h b/net/rds/tcp.h new file mode 100644 index 0000000..844fa6b --- /dev/null +++ b/net/rds/tcp.h @@ -0,0 +1,93 @@ +#ifndef _RDS_TCP_H +#define _RDS_TCP_H + +#define RDS_TCP_PORT 16385 + +struct rds_tcp_incoming { + struct rds_incoming ti_inc; + struct sk_buff_head ti_skb_list; +}; + +struct rds_tcp_connection { + + struct list_head t_tcp_node; + struct rds_connection *conn; + struct socket *t_sock; + void *t_orig_write_space; + void *t_orig_data_ready; + void *t_orig_state_change; + + struct rds_tcp_incoming *t_tinc; + size_t t_tinc_hdr_rem; + size_t t_tinc_data_rem; + + /* XXX error report? */ + struct work_struct t_conn_w; + struct work_struct t_send_w; + struct work_struct t_down_w; + struct work_struct t_recv_w; + + /* for info exporting only */ + struct list_head t_list_item; + u32 t_last_sent_nxt; + u32 t_last_expected_una; + u32 t_last_seen_una; +}; + +struct rds_tcp_statistics { + uint64_t s_tcp_data_ready_calls; + uint64_t s_tcp_write_space_calls; + uint64_t s_tcp_sndbuf_full; + uint64_t s_tcp_connect_raced; + uint64_t s_tcp_listen_closed_stale; +}; + +/* tcp.c */ +int __init rds_tcp_init(void); +void rds_tcp_exit(void); +void rds_tcp_tune(struct socket *sock); +void rds_tcp_nonagle(struct socket *sock); +void rds_tcp_set_callbacks(struct socket *sock, struct rds_connection *conn); +void rds_tcp_restore_callbacks(struct socket *sock, + struct rds_tcp_connection *tc); +u32 rds_tcp_snd_nxt(struct rds_tcp_connection *tc); +u32 rds_tcp_snd_una(struct rds_tcp_connection *tc); +u64 rds_tcp_map_seq(struct rds_tcp_connection *tc, u32 seq); +extern struct rds_transport rds_tcp_transport; + +/* tcp_connect.c */ +int rds_tcp_conn_connect(struct rds_connection *conn); +void rds_tcp_conn_shutdown(struct rds_connection *conn); +void rds_tcp_state_change(struct sock *sk); + +/* tcp_listen.c */ +int __init rds_tcp_listen_init(void); +void rds_tcp_listen_stop(void); +void rds_tcp_listen_data_ready(struct sock *sk, int bytes); + +/* tcp_recv.c */ +int __init rds_tcp_recv_init(void); +void rds_tcp_recv_exit(void); +void rds_tcp_data_ready(struct sock *sk, int bytes); +int rds_tcp_recv(struct rds_connection *conn); +void rds_tcp_inc_purge(struct rds_incoming *inc); +void rds_tcp_inc_free(struct rds_incoming *inc); +int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov, + size_t size); + +/* tcp_send.c */ +void rds_tcp_xmit_prepare(struct rds_connection *conn); +void rds_tcp_xmit_complete(struct rds_connection *conn); +int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, + unsigned int hdr_off, unsigned int sg, unsigned int off); +void rds_tcp_write_space(struct sock *sk); +int rds_tcp_xmit_cong_map(struct rds_connection *conn, + struct rds_cong_map *map, unsigned long offset); + +/* tcp_stats.c */ +DECLARE_PER_CPU(struct rds_tcp_statistics, rds_tcp_stats); +#define rds_tcp_stats_inc(member) rds_stats_inc_which(rds_tcp_stats, member) +unsigned int rds_tcp_stats_info_copy(struct rds_info_iterator *iter, + unsigned int avail); + +#endif diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c new file mode 100644 index 0000000..211522f --- /dev/null +++ b/net/rds/tcp_connect.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include + +#include "rds.h" +#include "tcp.h" + +void rds_tcp_state_change(struct sock *sk) +{ + void (*state_change)(struct sock *sk); + struct rds_connection *conn; + struct rds_tcp_connection *tc; + + read_lock(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (conn == NULL) { + state_change = sk->sk_state_change; + goto out; + } + tc = conn->c_transport_data; + state_change = tc->t_orig_state_change; + + rdsdebug("sock %p state_change to %d\n", tc->t_sock, sk->sk_state); + + switch(sk->sk_state) { + /* ignore connecting sockets as they make progress */ + case TCP_SYN_SENT: + case TCP_SYN_RECV: + break; + case TCP_ESTABLISHED: + rds_connect_complete(conn); + break; + case TCP_CLOSE: + rds_conn_drop(conn); + default: + break; + } +out: + read_unlock(&sk->sk_callback_lock); + state_change(sk); +} + +int rds_tcp_conn_connect(struct rds_connection *conn) +{ + struct socket *sock = NULL; + struct sockaddr_in src, dest; + int ret; + + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (ret < 0) + goto out; + + rds_tcp_tune(sock); + + src.sin_family = AF_INET; + src.sin_addr.s_addr = (__force u32)conn->c_laddr; + src.sin_port = (__force u16)htons(0); + + ret = sock->ops->bind(sock, (struct sockaddr *)&src, sizeof(src)); + if (ret) { + rdsdebug("bind failed with %d at address %u.%u.%u.%u\n", + ret, NIPQUAD(conn->c_laddr)); + goto out; + } + + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = (__force u32)conn->c_faddr; + dest.sin_port = (__force u16)htons(RDS_TCP_PORT); + + /* + * once we call connect() we can start getting callbacks and they + * own the socket + */ + rds_tcp_set_callbacks(sock, conn); + ret = sock->ops->connect(sock, (struct sockaddr *)&dest, sizeof(dest), + O_NONBLOCK); + sock = NULL; + + rdsdebug("connect to address %u.%u.%u.%u returned %d\n", + NIPQUAD(conn->c_faddr), ret); + if (ret == -EINPROGRESS) + ret = 0; + +out: + if (sock) + sock_release(sock); + return ret; +} + +/* + * Before killing the tcp socket this needs to serialize with callbacks. The + * caller has already grabbed the sending sem so we're serialized with other + * senders. + * + * TCP calls the callbacks with the sock lock so we hold it while we reset the + * callbacks to those set by TCP. Our callbacks won't execute again once we + * hold the sock lock. + */ +void rds_tcp_conn_shutdown(struct rds_connection *conn) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + struct socket *sock = tc->t_sock; + + rdsdebug("shutting down conn %p tc %p sock %p\n", conn, tc, sock); + + if (sock) { + sock->ops->shutdown(sock, RCV_SHUTDOWN | SEND_SHUTDOWN); + lock_sock(sock->sk); + rds_tcp_restore_callbacks(sock, tc); /* tc->tc_sock = NULL */ + + release_sock(sock->sk); + sock_release(sock); + }; + + if (tc->t_tinc) { + rds_inc_put(&tc->t_tinc->ti_inc); + tc->t_tinc = NULL; + } + tc->t_tinc_hdr_rem = sizeof(struct rds_header); + tc->t_tinc_data_rem = 0; +} diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c new file mode 100644 index 0000000..24b743e --- /dev/null +++ b/net/rds/tcp_listen.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include + +#include "rds.h" +#include "tcp.h" + +/* + * cheesy, but simple.. + */ +static void rds_tcp_accept_worker(struct work_struct *work); +static DECLARE_WORK(rds_tcp_listen_work, rds_tcp_accept_worker); +static struct socket *rds_tcp_listen_sock; + +static int rds_tcp_accept_one(struct socket *sock) +{ + struct socket *new_sock = NULL; + struct rds_connection *conn; + int ret; + struct inet_sock *inet; + + ret = sock_create_lite(sock->sk->sk_family, sock->sk->sk_type, + sock->sk->sk_protocol, &new_sock); + if (ret) + goto out; + + new_sock->type = sock->type; + new_sock->ops = sock->ops; + ret = sock->ops->accept(sock, new_sock, O_NONBLOCK); + if (ret < 0) + goto out; + + rds_tcp_tune(new_sock); + + inet = inet_sk(new_sock->sk); + + rdsdebug("accepted tcp %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport)); + + conn = rds_conn_create(inet->saddr, inet->daddr, &rds_tcp_transport, + GFP_KERNEL); + if (IS_ERR(conn)) { + ret = PTR_ERR(conn); + goto out; + } + + /* + * see the comment above rds_queue_delayed_reconnect() + */ + if (!rds_conn_transition(conn, RDS_CONN_DOWN, RDS_CONN_CONNECTING)) { + if (rds_conn_state(conn) == RDS_CONN_UP) + rds_tcp_stats_inc(s_tcp_listen_closed_stale); + else + rds_tcp_stats_inc(s_tcp_connect_raced); + rds_conn_drop(conn); + ret = 0; + goto out; + } + + rds_tcp_set_callbacks(new_sock, conn); + rds_connect_complete(conn); + new_sock = NULL; + ret = 0; + +out: + if (new_sock) + sock_release(new_sock); + return ret; +} + +static void rds_tcp_accept_worker(struct work_struct *work) +{ + while (rds_tcp_accept_one(rds_tcp_listen_sock) == 0) + cond_resched(); +} + +void rds_tcp_listen_data_ready(struct sock *sk, int bytes) +{ + void (*ready)(struct sock *sk, int bytes); + + rdsdebug("listen data ready sk %p\n", sk); + + read_lock(&sk->sk_callback_lock); + ready = sk->sk_user_data; + if (ready == NULL) { /* check for teardown race */ + ready = sk->sk_data_ready; + goto out; + } + + /* + * ->sk_data_ready is also called for a newly established child socket + * before it has been accepted and the accepter has set up their + * data_ready.. we only want to queue listen work for our listening + * socket + */ + if (sk->sk_state == TCP_LISTEN) + queue_work(rds_wq, &rds_tcp_listen_work); + +out: + read_unlock(&sk->sk_callback_lock); + ready(sk, bytes); +} + +int __init rds_tcp_listen_init(void) +{ + struct sockaddr_in sin; + struct socket *sock = NULL; + int ret; + + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (ret < 0) + goto out; + + sock->sk->sk_reuse = 1; + rds_tcp_nonagle(sock); + + write_lock_bh(&sock->sk->sk_callback_lock); + sock->sk->sk_user_data = sock->sk->sk_data_ready; + sock->sk->sk_data_ready = rds_tcp_listen_data_ready; + write_unlock_bh(&sock->sk->sk_callback_lock); + + sin.sin_family = PF_INET, + sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); + sin.sin_port = (__force u16)htons(RDS_TCP_PORT); + + ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin)); + if (ret < 0) + goto out; + + ret = sock->ops->listen(sock, 64); + if (ret < 0) + goto out; + + rds_tcp_listen_sock = sock; + sock = NULL; +out: + if (sock) + sock_release(sock); + return ret; +} + +void rds_tcp_listen_stop(void) +{ + struct socket *sock = rds_tcp_listen_sock; + struct sock *sk; + + if (sock == NULL) + return; + + sk = sock->sk; + + /* serialize with and prevent further callbacks */ + lock_sock(sk); + write_lock_bh(&sk->sk_callback_lock); + if (sk->sk_user_data) { + sk->sk_data_ready = sk->sk_user_data; + sk->sk_user_data = NULL; + } + write_unlock_bh(&sk->sk_callback_lock); + release_sock(sk); + + /* wait for accepts to stop and close the socket */ + flush_workqueue(rds_wq); + sock_release(sock); + rds_tcp_listen_sock = NULL; +} diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c new file mode 100644 index 0000000..c00daff --- /dev/null +++ b/net/rds/tcp_recv.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include + +#include "rds.h" +#include "tcp.h" + +static struct kmem_cache *rds_tcp_incoming_slab; + +void rds_tcp_inc_purge(struct rds_incoming *inc) +{ + struct rds_tcp_incoming *tinc; + tinc = container_of(inc, struct rds_tcp_incoming, ti_inc); + rdsdebug("purging tinc %p inc %p\n", tinc, inc); + skb_queue_purge(&tinc->ti_skb_list); +} + +void rds_tcp_inc_free(struct rds_incoming *inc) +{ + struct rds_tcp_incoming *tinc; + tinc = container_of(inc, struct rds_tcp_incoming, ti_inc); + rds_tcp_inc_purge(inc); + rdsdebug("freeing tinc %p inc %p\n", tinc, inc); + kmem_cache_free(rds_tcp_incoming_slab, tinc); +} + +/* + * this is pretty lame, but, whatever. + */ +int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, + size_t size) +{ + struct rds_tcp_incoming *tinc; + struct iovec *iov, tmp; + struct sk_buff *skb; + unsigned long to_copy, skb_off; + int ret = 0; + + if (size == 0) + goto out; + + tinc = container_of(inc, struct rds_tcp_incoming, ti_inc); + iov = first_iov; + tmp = *iov; + + skb_queue_walk(&tinc->ti_skb_list, skb) { + skb_off = 0; + while (skb_off < skb->len) { + while (tmp.iov_len == 0) { + iov++; + tmp = *iov; + } + + to_copy = min(tmp.iov_len, size); + to_copy = min(to_copy, skb->len - skb_off); + + rdsdebug("ret %d size %zu skb %p skb_off %lu " + "skblen %d iov_base %p iov_len %zu cpy %lu\n", + ret, size, skb, skb_off, skb->len, + tmp.iov_base, tmp.iov_len, to_copy); + + /* modifies tmp as it copies */ + if (skb_copy_datagram_iovec(skb, skb_off, &tmp, + to_copy)) { + ret = -EFAULT; + goto out; + } + + size -= to_copy; + ret += to_copy; + skb_off += to_copy; + if (size == 0) + goto out; + } + } +out: + return ret; +} + +/* + * We have a series of skbs that have fragmented pieces of the congestion + * bitmap. They must add up to the exact size of the congestion bitmap. We + * use the skb helpers to copy those into the pages that make up the in-memory + * congestion bitmap for the remote address of this connection. We then tell + * the congestion core that the bitmap has been changed so that it can wake up + * sleepers. + * + * This is racing with sending paths which are using test_bit to see if the + * bitmap indicates that their recipient is congested. + */ + +static void rds_tcp_cong_recv(struct rds_connection *conn, + struct rds_tcp_incoming *tinc) +{ + struct sk_buff *skb; + unsigned int to_copy, skb_off; + unsigned int map_off; + unsigned int map_page; + struct rds_cong_map *map; + int ret; + + /* catch completely corrupt packets */ + if (be32_to_cpu(tinc->ti_inc.i_hdr.h_len) != RDS_CONG_MAP_BYTES) + return; + + map_page = 0; + map_off = 0; + map = conn->c_fcong; + + skb_queue_walk(&tinc->ti_skb_list, skb) { + skb_off = 0; + while (skb_off < skb->len) { + to_copy = min_t(unsigned int, PAGE_SIZE - map_off, + skb->len - skb_off); + + BUG_ON(map_page >= RDS_CONG_MAP_PAGES); + + /* only returns 0 or -error */ + ret = skb_copy_bits(skb, skb_off, + (void *)map->m_page_addrs[map_page] + map_off, + to_copy); + BUG_ON(ret != 0); + + skb_off += to_copy; + map_off += to_copy; + if (map_off == PAGE_SIZE) { + map_off = 0; + map_page++; + } + } + } + + rds_cong_map_updated(map, ~(u64) 0); +} + +struct rds_tcp_desc_arg { + struct rds_connection *conn; + gfp_t gfp; + enum km_type km; +}; + +static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb, + unsigned int offset, size_t len) +{ + struct rds_tcp_desc_arg *arg = desc->arg.data; + struct rds_connection *conn = arg->conn; + struct rds_tcp_connection *tc = conn->c_transport_data; + struct rds_tcp_incoming *tinc = tc->t_tinc; + struct sk_buff *clone; + size_t left = len, to_copy; + + rdsdebug("tcp data tc %p skb %p offset %u len %zu\n", tc, skb, offset, + len); + + /* + * tcp_read_sock() interprets partial progress as an indication to stop + * processing. + */ + while (left) { + if (tinc == NULL) { + tinc = kmem_cache_alloc(rds_tcp_incoming_slab, + arg->gfp); + if (tinc == NULL) { + desc->error = -ENOMEM; + goto out; + } + tc->t_tinc = tinc; + rdsdebug("alloced tinc %p\n", tinc); + rds_inc_init(&tinc->ti_inc, conn, conn->c_faddr); + /* + * XXX * we might be able to use the __ variants when + * we've already serialized at a higher level. + */ + skb_queue_head_init(&tinc->ti_skb_list); + } + + if (left && tc->t_tinc_hdr_rem) { + to_copy = min(tc->t_tinc_hdr_rem, left); + rdsdebug("copying %zu header from skb %p\n", to_copy, + skb); + skb_copy_bits(skb, offset, + (char *)&tinc->ti_inc.i_hdr + + sizeof(struct rds_header) - + tc->t_tinc_hdr_rem, + to_copy); + tc->t_tinc_hdr_rem -= to_copy; + left -= to_copy; + offset += to_copy; + + if (tc->t_tinc_hdr_rem == 0) { + /* could be 0 for a 0 len message */ + tc->t_tinc_data_rem = + be32_to_cpu(tinc->ti_inc.i_hdr.h_len); + } + } + + if (left && tc->t_tinc_data_rem) { + clone = skb_clone(skb, arg->gfp); + if (clone == NULL) { + desc->error = -ENOMEM; + goto out; + } + + to_copy = min(tc->t_tinc_data_rem, left); + pskb_pull(clone, offset); + pskb_trim(clone, to_copy); + skb_queue_tail(&tinc->ti_skb_list, clone); + + rdsdebug("skb %p data %p len %d off %u to_copy %zu -> " + "clone %p data %p len %d\n", + skb, skb->data, skb->len, offset, to_copy, + clone, clone->data, clone->len); + + tc->t_tinc_data_rem -= to_copy; + left -= to_copy; + offset += to_copy; + } + + if (tc->t_tinc_hdr_rem == 0 && tc->t_tinc_data_rem == 0) { + if (tinc->ti_inc.i_hdr.h_flags == RDS_FLAG_CONG_BITMAP) + rds_tcp_cong_recv(conn, tinc); + else + rds_recv_incoming(conn, conn->c_faddr, + conn->c_laddr, &tinc->ti_inc, + arg->gfp, arg->km); + + tc->t_tinc_hdr_rem = sizeof(struct rds_header); + tc->t_tinc_data_rem = 0; + tc->t_tinc = NULL; + rds_inc_put(&tinc->ti_inc); + tinc = NULL; + } + } +out: + rdsdebug("returning len %zu left %zu skb len %d rx queue depth %d\n", + len, left, skb->len, + skb_queue_len(&tc->t_sock->sk->sk_receive_queue)); + return len - left; +} + +/* the caller has to hold the sock lock */ +int rds_tcp_read_sock(struct rds_connection *conn, gfp_t gfp, enum km_type km) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + struct socket *sock = tc->t_sock; + read_descriptor_t desc; + struct rds_tcp_desc_arg arg; + + /* It's like glib in the kernel! */ + arg.conn = conn; + arg.gfp = gfp; + arg.km = km; + desc.arg.data = &arg; + desc.error = 0; + desc.count = 1; /* give more than one skb per call */ + + tcp_read_sock(sock->sk, &desc, rds_tcp_data_recv); + rdsdebug("tcp_read_sock for tc %p gfp 0x%x returned %d\n", tc, gfp, + desc.error); + + return desc.error; +} + +/* + * We hold the sock lock to serialize our rds_tcp_recv->tcp_read_sock from + * data_ready. + * + * if we fail to allocate we're in trouble.. blindly wait some time before + * trying again to see if the VM can free up something for us. + */ +int rds_tcp_recv(struct rds_connection *conn) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + struct socket *sock = tc->t_sock; + int ret = 0; + + rdsdebug("recv worker conn %p tc %p sock %p\n", conn, tc, sock); + + lock_sock(sock->sk); + ret = rds_tcp_read_sock(conn, GFP_KERNEL, KM_USER0); + release_sock(sock->sk); + + return ret; +} + +void rds_tcp_data_ready(struct sock *sk, int bytes) +{ + void (*ready)(struct sock *sk, int bytes); + struct rds_connection *conn; + struct rds_tcp_connection *tc; + + rdsdebug("data ready sk %p bytes %d\n", sk, bytes); + + read_lock(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (conn == NULL) { /* check for teardown race */ + ready = sk->sk_data_ready; + goto out; + } + + tc = conn->c_transport_data; + ready = tc->t_orig_data_ready; + rds_tcp_stats_inc(s_tcp_data_ready_calls); + + if (rds_tcp_read_sock(conn, GFP_ATOMIC, KM_SOFTIRQ0) == -ENOMEM) + queue_delayed_work(rds_wq, &conn->c_recv_w, 0); +out: + read_unlock(&sk->sk_callback_lock); + ready(sk, bytes); +} + +int __init rds_tcp_recv_init(void) +{ + rds_tcp_incoming_slab = kmem_cache_create("rds_tcp_incoming", + sizeof(struct rds_tcp_incoming), + 0, 0, NULL); + if (rds_tcp_incoming_slab == NULL) + return -ENOMEM; + return 0; +} + +void rds_tcp_recv_exit(void) +{ + kmem_cache_destroy(rds_tcp_incoming_slab); +} diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c new file mode 100644 index 0000000..ab545e0 --- /dev/null +++ b/net/rds/tcp_send.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include + +#include "rds.h" +#include "tcp.h" + +static void rds_tcp_cork(struct socket *sock, int val) +{ + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + sock->ops->setsockopt(sock, SOL_TCP, TCP_CORK, (char __user *)&val, + sizeof(val)); + set_fs(oldfs); +} + +void rds_tcp_xmit_prepare(struct rds_connection *conn) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + + rds_tcp_cork(tc->t_sock, 1); +} + +void rds_tcp_xmit_complete(struct rds_connection *conn) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + + rds_tcp_cork(tc->t_sock, 0); +} + +/* the core send_sem serializes this with other xmit and shutdown */ +int rds_tcp_sendmsg(struct socket *sock, void *data, unsigned int len) +{ + struct kvec vec = { + .iov_base = data, + .iov_len = len, + }; + struct msghdr msg = { + .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL, + }; + + return kernel_sendmsg(sock, &msg, &vec, 1, vec.iov_len); +} + +/* the core send_sem serializes this with other xmit and shutdown */ +int rds_tcp_xmit_cong_map(struct rds_connection *conn, + struct rds_cong_map *map, unsigned long offset) +{ + static struct rds_header rds_tcp_map_header = { + .h_flags = RDS_FLAG_CONG_BITMAP, + }; + struct rds_tcp_connection *tc = conn->c_transport_data; + unsigned long i; + int ret; + int copied = 0; + + /* Some problem claims cpu_to_be32(constant) isn't a constant. */ + rds_tcp_map_header.h_len = cpu_to_be32(RDS_CONG_MAP_BYTES); + + if (offset < sizeof(struct rds_header)) { + ret = rds_tcp_sendmsg(tc->t_sock, + (void *)&rds_tcp_map_header + offset, + sizeof(struct rds_header) - offset); + if (ret <= 0) + return ret; + offset += ret; + copied = ret; + if (offset < sizeof(struct rds_header)) + return ret; + } + + offset -= sizeof(struct rds_header); + i = offset / PAGE_SIZE; + offset = offset % PAGE_SIZE; + BUG_ON(i >= RDS_CONG_MAP_PAGES); + + do { + ret = tc->t_sock->ops->sendpage(tc->t_sock, + virt_to_page(map->m_page_addrs[i]), + offset, PAGE_SIZE - offset, + MSG_DONTWAIT); + if (ret <= 0) + break; + copied += ret; + offset += ret; + if (offset == PAGE_SIZE) { + offset = 0; + i++; + } + } while (i < RDS_CONG_MAP_PAGES); + + return copied ? copied : ret; +} + +/* the core send_sem serializes this with other xmit and shutdown */ +int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, + unsigned int hdr_off, unsigned int sg, unsigned int off) +{ + struct rds_tcp_connection *tc = conn->c_transport_data; + int done = 0; + int ret = 0; + + if (hdr_off == 0) { + /* + * m_ack_seq is set to the sequence number of the last byte of + * header and data. see rds_tcp_is_acked(). + */ + tc->t_last_sent_nxt = rds_tcp_snd_nxt(tc); + rm->m_ack_seq = tc->t_last_sent_nxt + + sizeof(struct rds_header) + + be32_to_cpu(rm->m_inc.i_hdr.h_len) - 1; + smp_mb__before_clear_bit(); + set_bit(RDS_MSG_HAS_ACK_SEQ, &rm->m_flags); + tc->t_last_expected_una = rm->m_ack_seq + 1; + + rdsdebug("rm %p tcp nxt %u ack_seq %llu\n", + rm, rds_tcp_snd_nxt(tc), + (unsigned long long)rm->m_ack_seq); + } + + if (hdr_off < sizeof(struct rds_header)) { + /* see rds_tcp_write_space() */ + set_bit(SOCK_NOSPACE, &tc->t_sock->sk->sk_socket->flags); + + ret = rds_tcp_sendmsg(tc->t_sock, + (void *)&rm->m_inc.i_hdr + hdr_off, + sizeof(rm->m_inc.i_hdr) - hdr_off); + if (ret < 0) + goto out; + done += ret; + if (hdr_off + done != sizeof(struct rds_header)) + goto out; + } + + while (sg < rm->m_nents) { + ret = tc->t_sock->ops->sendpage(tc->t_sock, + sg_page(&rm->m_sg[sg]), + rm->m_sg[sg].offset + off, + rm->m_sg[sg].length - off, + MSG_DONTWAIT|MSG_NOSIGNAL); + rdsdebug("tcp sendpage %p:%u:%u ret %d\n", (void *)sg_page(&rm->m_sg[sg]), + rm->m_sg[sg].offset + off, rm->m_sg[sg].length - off, + ret); + if (ret <= 0) + break; + + off += ret; + done += ret; + if (off == rm->m_sg[sg].length) { + off = 0; + sg++; + } + } + +out: + if (ret <= 0) { + /* write_space will hit after EAGAIN, all else fatal */ + if (ret == -EAGAIN) { + rds_tcp_stats_inc(s_tcp_sndbuf_full); + ret = 0; + } else { + printk(KERN_WARNING "RDS/tcp: send to %u.%u.%u.%u " + "returned %d, disconnecting and reconnecting\n", + NIPQUAD(conn->c_faddr), ret); + rds_conn_drop(conn); + } + } + if (done == 0) + done = ret; + return done; +} + +/* + * rm->m_ack_seq is set to the tcp sequence number that corresponds to the + * last byte of the message, including the header. This means that the + * entire message has been received if rm->m_ack_seq is "before" the next + * unacked byte of the TCP sequence space. We have to do very careful + * wrapping 32bit comparisons here. + */ +static int rds_tcp_is_acked(struct rds_message *rm, uint64_t ack) +{ + if (!test_bit(RDS_MSG_HAS_ACK_SEQ, &rm->m_flags)) + return 0; + return (__s32)((u32)rm->m_ack_seq - (u32)ack) < 0; +} + +void rds_tcp_write_space(struct sock *sk) +{ + void (*write_space)(struct sock *sk); + struct rds_connection *conn; + struct rds_tcp_connection *tc; + + read_lock(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (conn == NULL) { + write_space = sk->sk_write_space; + goto out; + } + + tc = conn->c_transport_data; + rdsdebug("write_space for tc %p\n", tc); + write_space = tc->t_orig_write_space; + rds_tcp_stats_inc(s_tcp_write_space_calls); + + rdsdebug("tcp una %u\n", rds_tcp_snd_una(tc)); + tc->t_last_seen_una = rds_tcp_snd_una(tc); + rds_send_drop_acked(conn, rds_tcp_snd_una(tc), rds_tcp_is_acked); + + queue_delayed_work(rds_wq, &conn->c_send_w, 0); +out: + read_unlock(&sk->sk_callback_lock); + + /* + * write_space is only called when data leaves tcp's send queue if + * SOCK_NOSPACE is set. We set SOCK_NOSPACE every time we put + * data in tcp's send queue because we use write_space to parse the + * sequence numbers and notice that rds messages have been fully + * received. + * + * tcp's write_space clears SOCK_NOSPACE if the send queue has more + * than a certain amount of space. So we need to set it again *after* + * we call tcp's write_space or else we might only get called on the + * first of a series of incoming tcp acks. + */ + write_space(sk); + + if (sk->sk_socket) + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); +} diff --git a/net/rds/tcp_stats.c b/net/rds/tcp_stats.c new file mode 100644 index 0000000..d5898d0 --- /dev/null +++ b/net/rds/tcp_stats.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include + +#include "rds.h" +#include "tcp.h" + +DEFINE_PER_CPU(struct rds_tcp_statistics, rds_tcp_stats) + ____cacheline_aligned; + +static const char const *rds_tcp_stat_names[] = { + "tcp_data_ready_calls", + "tcp_write_space_calls", + "tcp_sndbuf_full", + "tcp_connect_raced", + "tcp_listen_closed_stale", +}; + +unsigned int rds_tcp_stats_info_copy(struct rds_info_iterator *iter, + unsigned int avail) +{ + struct rds_tcp_statistics stats = {0, }; + uint64_t *src; + uint64_t *sum; + size_t i; + int cpu; + + if (avail < ARRAY_SIZE(rds_tcp_stat_names)) + goto out; + + for_each_online_cpu(cpu) { + src = (uint64_t *)&(per_cpu(rds_tcp_stats, cpu)); + sum = (uint64_t *)&stats; + for (i = 0; i < sizeof(stats) / sizeof(uint64_t); i++) + *(sum++) += *(src++); + } + + rds_stats_info_copy(iter, (uint64_t *)&stats, rds_tcp_stat_names, + ARRAY_SIZE(rds_tcp_stat_names)); +out: + return ARRAY_SIZE(rds_tcp_stat_names); +} -- cgit v1.1 From 616b757ae18fb8ec2dfe7ff9d3f589f82cb0eb9d Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 21 Aug 2009 12:28:32 +0000 Subject: RDS: Export symbols from core RDS Now that rdma and tcp transports will be modularized, we need to export a number of functions so they can call them. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/cong.c | 1 + net/rds/connection.c | 5 +++++ net/rds/info.c | 3 +++ net/rds/message.c | 6 ++++++ net/rds/page.c | 1 + net/rds/recv.c | 4 ++++ net/rds/send.c | 3 +++ net/rds/stats.c | 2 ++ net/rds/threads.c | 2 ++ net/rds/transport.c | 2 ++ 10 files changed, 29 insertions(+) (limited to 'net') diff --git a/net/rds/cong.c b/net/rds/cong.c index 710e459..dd2711d 100644 --- a/net/rds/cong.c +++ b/net/rds/cong.c @@ -254,6 +254,7 @@ void rds_cong_map_updated(struct rds_cong_map *map, uint64_t portmask) read_unlock_irqrestore(&rds_cong_monitor_lock, flags); } } +EXPORT_SYMBOL_GPL(rds_cong_map_updated); int rds_cong_updated_since(unsigned long *recent) { diff --git a/net/rds/connection.c b/net/rds/connection.c index b420a20..cc8b568 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -255,12 +255,14 @@ struct rds_connection *rds_conn_create(__be32 laddr, __be32 faddr, { return __rds_conn_create(laddr, faddr, trans, gfp, 0); } +EXPORT_SYMBOL_GPL(rds_conn_create); struct rds_connection *rds_conn_create_outgoing(__be32 laddr, __be32 faddr, struct rds_transport *trans, gfp_t gfp) { return __rds_conn_create(laddr, faddr, trans, gfp, 1); } +EXPORT_SYMBOL_GPL(rds_conn_create_outgoing); void rds_conn_destroy(struct rds_connection *conn) { @@ -303,6 +305,7 @@ void rds_conn_destroy(struct rds_connection *conn) rds_conn_count--; } +EXPORT_SYMBOL_GPL(rds_conn_destroy); static void rds_conn_message_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, @@ -406,6 +409,7 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, spin_unlock_irqrestore(&rds_conn_lock, flags); } +EXPORT_SYMBOL_GPL(rds_for_each_conn_info); static int rds_conn_info_visitor(struct rds_connection *conn, void *buffer) @@ -481,6 +485,7 @@ void rds_conn_drop(struct rds_connection *conn) atomic_set(&conn->c_state, RDS_CONN_ERROR); queue_work(rds_wq, &conn->c_down_w); } +EXPORT_SYMBOL_GPL(rds_conn_drop); /* * An error occurred on the connection diff --git a/net/rds/info.c b/net/rds/info.c index 62aeef3..814a91a 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -79,6 +79,7 @@ void rds_info_register_func(int optname, rds_info_func func) rds_info_funcs[offset] = func; spin_unlock(&rds_info_lock); } +EXPORT_SYMBOL_GPL(rds_info_register_func); void rds_info_deregister_func(int optname, rds_info_func func) { @@ -91,6 +92,7 @@ void rds_info_deregister_func(int optname, rds_info_func func) rds_info_funcs[offset] = NULL; spin_unlock(&rds_info_lock); } +EXPORT_SYMBOL_GPL(rds_info_deregister_func); /* * Typically we hold an atomic kmap across multiple rds_info_copy() calls @@ -137,6 +139,7 @@ void rds_info_copy(struct rds_info_iterator *iter, void *data, } } } +EXPORT_SYMBOL_GPL(rds_info_copy); /* * @optval points to the userspace buffer that the information snapshot diff --git a/net/rds/message.c b/net/rds/message.c index 5a15dc8..ca50a8e 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -50,6 +50,7 @@ void rds_message_addref(struct rds_message *rm) rdsdebug("addref rm %p ref %d\n", rm, atomic_read(&rm->m_refcount)); atomic_inc(&rm->m_refcount); } +EXPORT_SYMBOL_GPL(rds_message_addref); /* * This relies on dma_map_sg() not touching sg[].page during merging. @@ -92,6 +93,7 @@ void rds_message_put(struct rds_message *rm) kfree(rm); } } +EXPORT_SYMBOL_GPL(rds_message_put); void rds_message_inc_free(struct rds_incoming *inc) { @@ -108,6 +110,7 @@ void rds_message_populate_header(struct rds_header *hdr, __be16 sport, hdr->h_sequence = cpu_to_be64(seq); hdr->h_exthdr[0] = RDS_EXTHDR_NONE; } +EXPORT_SYMBOL_GPL(rds_message_populate_header); int rds_message_add_extension(struct rds_header *hdr, unsigned int type, const void *data, unsigned int len) @@ -133,6 +136,7 @@ int rds_message_add_extension(struct rds_header *hdr, dst[len] = RDS_EXTHDR_NONE; return 1; } +EXPORT_SYMBOL_GPL(rds_message_add_extension); /* * If a message has extension headers, retrieve them here. @@ -208,6 +212,7 @@ int rds_message_add_rdma_dest_extension(struct rds_header *hdr, u32 r_key, u32 o ext_hdr.h_rdma_offset = cpu_to_be32(offset); return rds_message_add_extension(hdr, RDS_EXTHDR_RDMA_DEST, &ext_hdr, sizeof(ext_hdr)); } +EXPORT_SYMBOL_GPL(rds_message_add_rdma_dest_extension); struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp) { @@ -399,4 +404,5 @@ void rds_message_unmapped(struct rds_message *rm) if (waitqueue_active(&rds_message_flush_waitq)) wake_up(&rds_message_flush_waitq); } +EXPORT_SYMBOL_GPL(rds_message_unmapped); diff --git a/net/rds/page.c b/net/rds/page.c index c460743..55c21ef 100644 --- a/net/rds/page.c +++ b/net/rds/page.c @@ -81,6 +81,7 @@ int rds_page_copy_user(struct page *page, unsigned long offset, return 0; } +EXPORT_SYMBOL_GPL(rds_page_copy_user); /* * Message allocation uses this to build up regions of a message. diff --git a/net/rds/recv.c b/net/rds/recv.c index 86bc1a0..fdff33c 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -46,12 +46,14 @@ void rds_inc_init(struct rds_incoming *inc, struct rds_connection *conn, inc->i_saddr = saddr; inc->i_rdma_cookie = 0; } +EXPORT_SYMBOL_GPL(rds_inc_init); void rds_inc_addref(struct rds_incoming *inc) { rdsdebug("addref inc %p ref %d\n", inc, atomic_read(&inc->i_refcount)); atomic_inc(&inc->i_refcount); } +EXPORT_SYMBOL_GPL(rds_inc_addref); void rds_inc_put(struct rds_incoming *inc) { @@ -62,6 +64,7 @@ void rds_inc_put(struct rds_incoming *inc) inc->i_conn->c_trans->inc_free(inc); } } +EXPORT_SYMBOL_GPL(rds_inc_put); static void rds_recv_rcvbuf_delta(struct rds_sock *rs, struct sock *sk, struct rds_cong_map *map, @@ -237,6 +240,7 @@ out: if (rs) rds_sock_put(rs); } +EXPORT_SYMBOL_GPL(rds_recv_incoming); /* * be very careful here. This is being called as the condition in diff --git a/net/rds/send.c b/net/rds/send.c index a4a7f42..28c88ff 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -439,6 +439,7 @@ void rds_rdma_send_complete(struct rds_message *rm, int status) sock_put(rds_rs_to_sk(rs)); } } +EXPORT_SYMBOL_GPL(rds_rdma_send_complete); /* * This is the same as rds_rdma_send_complete except we @@ -494,6 +495,7 @@ out: return found; } +EXPORT_SYMBOL_GPL(rds_send_get_message); /* * This removes messages from the socket's list if they're on it. The list @@ -610,6 +612,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack, /* now remove the messages from the sock list as needed */ rds_send_remove_from_sock(&list, RDS_RDMA_SUCCESS); } +EXPORT_SYMBOL_GPL(rds_send_drop_acked); void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest) { diff --git a/net/rds/stats.c b/net/rds/stats.c index 91d8c58..7598eb0 100644 --- a/net/rds/stats.c +++ b/net/rds/stats.c @@ -37,6 +37,7 @@ #include "rds.h" DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_statistics, rds_stats); +EXPORT_PER_CPU_SYMBOL_GPL(rds_stats); /* :.,$s/unsigned long\>.*\c_send_w, 0); queue_delayed_work(rds_wq, &conn->c_recv_w, 0); } +EXPORT_SYMBOL_GPL(rds_connect_complete); /* * This random exponential backoff is relied on to eventually resolve racing diff --git a/net/rds/transport.c b/net/rds/transport.c index 767da61..56a5309 100644 --- a/net/rds/transport.c +++ b/net/rds/transport.c @@ -53,6 +53,7 @@ int rds_trans_register(struct rds_transport *trans) return 0; } +EXPORT_SYMBOL_GPL(rds_trans_register); void rds_trans_unregister(struct rds_transport *trans) { @@ -63,6 +64,7 @@ void rds_trans_unregister(struct rds_transport *trans) up_write(&rds_trans_sem); } +EXPORT_SYMBOL_GPL(rds_trans_unregister); struct rds_transport *rds_trans_get_preferred(__be32 addr) { -- cgit v1.1 From 40d866095df3bb70ded1813f4852cab445ef678b Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 21 Aug 2009 12:28:33 +0000 Subject: RDS: Modularize RDMA and TCP transports Enable the building of transports as modules. Also, improve consistency of Kconfig messages in relation to other protocols, and move build dependency on IB from the RDS core code to the rds_rdma module. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/Kconfig | 26 ++++++++++++++++++++------ net/rds/Makefile | 11 +++++++++-- net/rds/af_rds.c | 8 -------- net/rds/rdma_transport.c | 6 ++++++ 4 files changed, 35 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/rds/Kconfig b/net/rds/Kconfig index 796773b..ec753b3 100644 --- a/net/rds/Kconfig +++ b/net/rds/Kconfig @@ -1,14 +1,28 @@ config RDS - tristate "Reliable Datagram Sockets (RDS) (EXPERIMENTAL)" - depends on INET && INFINIBAND_IPOIB && EXPERIMENTAL - depends on INFINIBAND && INFINIBAND_ADDR_TRANS + tristate "The RDS Protocol (EXPERIMENTAL)" + depends on INET && EXPERIMENTAL ---help--- - RDS provides reliable, sequenced delivery of datagrams - over Infiniband. + The RDS (Reliable Datagram Sockets) protocol provides reliable, + sequenced delivery of datagrams over Infiniband, iWARP, + or TCP. + +config RDS_RDMA + tristate "RDS over Infiniband and iWARP" + depends on RDS && INFINIBAND && INFINIBAND_ADDR_TRANS + ---help--- + Allow RDS to use Infiniband and iWARP as a transport. + This transport supports RDMA operations. + +config RDS_TCP + tristate "RDS over TCP" + depends on RDS + ---help--- + Allow RDS to use TCP as a transport. + This transport does not support RDMA operations. config RDS_DEBUG - bool "Debugging messages" + bool "RDS debugging messages" depends on RDS default n diff --git a/net/rds/Makefile b/net/rds/Makefile index 51f2758..b46eca1 100644 --- a/net/rds/Makefile +++ b/net/rds/Makefile @@ -1,13 +1,20 @@ obj-$(CONFIG_RDS) += rds.o rds-y := af_rds.o bind.o cong.o connection.o info.o message.o \ recv.o send.o stats.o sysctl.o threads.o transport.o \ - loop.o page.o rdma.o \ - rdma_transport.o \ + loop.o page.o rdma.o + +obj-$(CONFIG_RDS_RDMA) += rds_rdma.o +rds_rdma-objs := rdma_transport.o \ ib.o ib_cm.o ib_recv.o ib_ring.o ib_send.o ib_stats.o \ ib_sysctl.o ib_rdma.o \ iw.o iw_cm.o iw_recv.o iw_ring.o iw_send.o iw_stats.o \ iw_sysctl.o iw_rdma.o + +obj-$(CONFIG_RDS_TCP) += rds_tcp.o +rds_tcp-objs := tcp.o tcp_connect.o tcp_listen.o tcp_recv.o \ + tcp_send.o tcp_stats.o + ifeq ($(CONFIG_RDS_DEBUG), y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index b11e7e5..108ed2e 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -39,7 +39,6 @@ #include "rds.h" #include "rdma.h" -#include "rdma_transport.h" /* this is just used for stats gathering :/ */ static DEFINE_SPINLOCK(rds_sock_lock); @@ -509,7 +508,6 @@ out: static void __exit rds_exit(void) { - rds_rdma_exit(); sock_unregister(rds_family_ops.family); proto_unregister(&rds_proto); rds_conn_exit(); @@ -549,14 +547,8 @@ static int __init rds_init(void) rds_info_register_func(RDS_INFO_SOCKETS, rds_sock_info); rds_info_register_func(RDS_INFO_RECV_MESSAGES, rds_sock_inc_info); - /* ib/iwarp transports currently compiled-in */ - ret = rds_rdma_init(); - if (ret) - goto out_sock; goto out; -out_sock: - sock_unregister(rds_family_ops.family); out_proto: proto_unregister(&rds_proto); out_stats: diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index 7a6c748..9ece910 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -203,6 +203,7 @@ err_iw_init: out: return ret; } +module_init(rds_rdma_init); void rds_rdma_exit(void) { @@ -211,4 +212,9 @@ void rds_rdma_exit(void) rds_ib_exit(); rds_iw_exit(); } +module_exit(rds_rdma_exit); + +MODULE_AUTHOR("Oracle Corporation "); +MODULE_DESCRIPTION("RDS: IB/iWARP transport"); +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.1 From 335776bd696a6bf95134baf8ad95847371e4d5f6 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 21 Aug 2009 12:28:34 +0000 Subject: RDS: Track transports via an array, not a list Now that transports can be loaded in arbitrary order, it is important for rds_trans_get_preferred() to look for them in a particular order, instead of walking the list until it finds a transport that works for a given address. Now, each transport registers for a specific transport slot, and these are ordered so that preferred transports come first, and then if they are not loaded, other transports are queried. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/ib.c | 1 + net/rds/iw.c | 1 + net/rds/rds.h | 6 ++++++ net/rds/tcp.c | 1 + net/rds/transport.c | 29 +++++++++++++++++++---------- 5 files changed, 28 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/rds/ib.c b/net/rds/ib.c index 868559a..536ebe5 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -282,6 +282,7 @@ struct rds_transport rds_ib_transport = { .flush_mrs = rds_ib_flush_mrs, .t_owner = THIS_MODULE, .t_name = "infiniband", + .t_type = RDS_TRANS_IB }; int __init rds_ib_init(void) diff --git a/net/rds/iw.c b/net/rds/iw.c index f5e9a29..db224f7 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -284,6 +284,7 @@ struct rds_transport rds_iw_transport = { .flush_mrs = rds_iw_flush_mrs, .t_owner = THIS_MODULE, .t_name = "iwarp", + .t_type = RDS_TRANS_IWARP, .t_prefer_loopback = 1, }; diff --git a/net/rds/rds.h b/net/rds/rds.h index 290566c..85d6f89 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -311,11 +311,17 @@ struct rds_notifier { * flag and header. */ +#define RDS_TRANS_IB 0 +#define RDS_TRANS_IWARP 1 +#define RDS_TRANS_TCP 2 +#define RDS_TRANS_COUNT 3 + struct rds_transport { char t_name[TRANSNAMSIZ]; struct list_head t_item; struct module *t_owner; unsigned int t_prefer_loopback:1; + unsigned int t_type; int (*laddr_check)(__be32 addr); int (*conn_alloc)(struct rds_connection *conn, gfp_t gfp); diff --git a/net/rds/tcp.c b/net/rds/tcp.c index e0ac900..b5198ae 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -271,6 +271,7 @@ struct rds_transport rds_tcp_transport = { .exit = rds_tcp_exit, .t_owner = THIS_MODULE, .t_name = "tcp", + .t_type = RDS_TRANS_TCP, .t_prefer_loopback = 1, }; diff --git a/net/rds/transport.c b/net/rds/transport.c index 56a5309..7e10679 100644 --- a/net/rds/transport.c +++ b/net/rds/transport.c @@ -37,7 +37,7 @@ #include "rds.h" #include "loop.h" -static LIST_HEAD(rds_transports); +static struct rds_transport *transports[RDS_TRANS_COUNT]; static DECLARE_RWSEM(rds_trans_sem); int rds_trans_register(struct rds_transport *trans) @@ -46,8 +46,13 @@ int rds_trans_register(struct rds_transport *trans) down_write(&rds_trans_sem); - list_add_tail(&trans->t_item, &rds_transports); - printk(KERN_INFO "Registered RDS/%s transport\n", trans->t_name); + if (transports[trans->t_type]) + printk(KERN_ERR "RDS Transport type %d already registered\n", + trans->t_type); + else { + transports[trans->t_type] = trans; + printk(KERN_INFO "Registered RDS/%s transport\n", trans->t_name); + } up_write(&rds_trans_sem); @@ -59,7 +64,7 @@ void rds_trans_unregister(struct rds_transport *trans) { down_write(&rds_trans_sem); - list_del_init(&trans->t_item); + transports[trans->t_type] = NULL; printk(KERN_INFO "Unregistered RDS/%s transport\n", trans->t_name); up_write(&rds_trans_sem); @@ -68,16 +73,17 @@ EXPORT_SYMBOL_GPL(rds_trans_unregister); struct rds_transport *rds_trans_get_preferred(__be32 addr) { - struct rds_transport *trans; struct rds_transport *ret = NULL; + int i; if (IN_LOOPBACK(ntohl(addr))) return &rds_loop_transport; down_read(&rds_trans_sem); - list_for_each_entry(trans, &rds_transports, t_item) { - if (trans->laddr_check(addr) == 0) { - ret = trans; + for (i = 0; i < RDS_TRANS_COUNT; i++) + { + if (transports[i] && (transports[i]->laddr_check(addr) == 0)) { + ret = transports[i]; break; } } @@ -99,12 +105,15 @@ unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter, struct rds_transport *trans; unsigned int total = 0; unsigned int part; + int i; rds_info_iter_unmap(iter); down_read(&rds_trans_sem); - list_for_each_entry(trans, &rds_transports, t_item) { - if (trans->stats_info_copy == NULL) + for (i = 0; i < RDS_TRANS_COUNT; i++) + { + trans = transports[i]; + if (!trans || !trans->stats_info_copy) continue; part = trans->stats_info_copy(iter, avail); -- cgit v1.1 From f2c449320d547bd5c281649eb1d99afb20765144 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Fri, 21 Aug 2009 12:28:35 +0000 Subject: RDS: Add a debug message suggesting to load transport modules Now that RDS transports are no longer compiled-in to RDS core, there is now the possibility that they will not be loaded. This adds a helpful suggestion when rds_bind() fails to find a transport. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- net/rds/bind.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/rds/bind.c b/net/rds/bind.c index c17cc39..5d95fc0 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -187,6 +187,9 @@ int rds_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (trans == NULL) { ret = -EADDRNOTAVAIL; rds_remove_bound(rs); + if (printk_ratelimit()) + printk(KERN_INFO "RDS: rds_bind() could not find a transport, " + "load rds_tcp or rds_rdma?\n"); goto out; } -- cgit v1.1 From e686219a64fee9be9ce438dc3f040cd71ddd168a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 24 Aug 2009 00:45:19 -0300 Subject: Bluetooth: Add locking scheme to L2CAP timeout callbacks Avoid race conditions when accessing the L2CAP socket from within the timeout handlers. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c04526f..efac637 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1192,6 +1192,7 @@ static void l2cap_monitor_timeout(unsigned long arg) struct sock *sk = (void *) arg; u16 control; + bh_lock_sock(sk); if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk); return; @@ -1203,6 +1204,7 @@ static void l2cap_monitor_timeout(unsigned long arg) control = L2CAP_CTRL_POLL; control |= L2CAP_SUPER_RCV_READY; l2cap_send_sframe(l2cap_pi(sk), control); + bh_unlock_sock(sk); } static void l2cap_retrans_timeout(unsigned long arg) @@ -1210,6 +1212,7 @@ static void l2cap_retrans_timeout(unsigned long arg) struct sock *sk = (void *) arg; u16 control; + bh_lock_sock(sk); l2cap_pi(sk)->retry_count = 1; __mod_monitor_timer(); @@ -1218,6 +1221,7 @@ static void l2cap_retrans_timeout(unsigned long arg) control = L2CAP_CTRL_POLL; control |= L2CAP_SUPER_RCV_READY; l2cap_send_sframe(l2cap_pi(sk), control); + bh_unlock_sock(sk); } static void l2cap_drop_acked_frames(struct sock *sk) -- cgit v1.1 From 1b7bf4edca0fdbad70c44e139f4cfebd6759de81 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 24 Aug 2009 00:45:20 -0300 Subject: Bluetooth: Use proper *_unaligned_le{16,32} helpers for L2CAP Simplify more conversions to the right endian with the proper helpers. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index efac637..e5847c5 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -2924,7 +2924,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm if (enable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; - put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data); + put_unaligned_le32(feat_mask, rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else if (type == L2CAP_IT_FIXED_CHAN) { @@ -3572,7 +3572,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) break; case L2CAP_CID_CONN_LESS: - psm = get_unaligned((__le16 *) skb->data); + psm = get_unaligned_le16(skb->data); skb_pull(skb, 2); l2cap_conless_channel(conn, psm, skb); break; -- cgit v1.1 From 35aad0ffdf548617940ca1e78be1f2e0bafc4496 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 24 Aug 2009 14:56:30 +0200 Subject: netfilter: xtables: mark initial tables constant The inputted table is never modified, so should be considered const. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- net/bridge/netfilter/ebtable_broute.c | 2 +- net/bridge/netfilter/ebtable_filter.c | 2 +- net/bridge/netfilter/ebtables.c | 13 +++++++------ net/ipv4/netfilter/arp_tables.c | 3 ++- net/ipv4/netfilter/arptable_filter.c | 4 ++-- net/ipv4/netfilter/ip_tables.c | 3 ++- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 4 ++-- net/ipv4/netfilter/iptable_raw.c | 4 ++-- net/ipv4/netfilter/iptable_security.c | 4 ++-- net/ipv4/netfilter/nf_nat_rule.c | 4 ++-- net/ipv6/netfilter/ip6_tables.c | 3 ++- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 4 ++-- net/ipv6/netfilter/ip6table_raw.c | 4 ++-- net/ipv6/netfilter/ip6table_security.c | 4 ++-- net/netfilter/x_tables.c | 7 ++++--- 17 files changed, 37 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index c751111..d32ab13 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -41,7 +41,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks) return 0; } -static struct ebt_table broute_table = +static const struct ebt_table broute_table = { .name = "broute", .table = &initial_table, diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 4b988db..60b1a6c 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -50,7 +50,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks) return 0; } -static struct ebt_table frame_filter = +static const struct ebt_table frame_filter = { .name = "filter", .table = &initial_table, diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 37928d5..bd1c654 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1103,23 +1103,24 @@ free_newinfo: return ret; } -struct ebt_table *ebt_register_table(struct net *net, struct ebt_table *table) +struct ebt_table * +ebt_register_table(struct net *net, const struct ebt_table *input_table) { struct ebt_table_info *newinfo; - struct ebt_table *t; + struct ebt_table *t, *table; struct ebt_replace_kernel *repl; int ret, i, countersize; void *p; - if (!table || !(repl = table->table) || !repl->entries || - repl->entries_size == 0 || - repl->counters || table->private) { + if (input_table == NULL || (repl = input_table->table) == NULL || + repl->entries == 0 || repl->entries_size == 0 || + repl->counters != NULL || input_table->private != NULL) { BUGPRINT("Bad table data for ebt_register_table!!!\n"); return ERR_PTR(-EINVAL); } /* Don't add one table to multiple lists. */ - table = kmemdup(table, sizeof(struct ebt_table), GFP_KERNEL); + table = kmemdup(input_table, sizeof(struct ebt_table), GFP_KERNEL); if (!table) { ret = -ENOMEM; goto out; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 7bc11ff..27774c9 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1778,7 +1778,8 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len return ret; } -struct xt_table *arpt_register_table(struct net *net, struct xt_table *table, +struct xt_table *arpt_register_table(struct net *net, + const struct xt_table *table, const struct arpt_replace *repl) { int ret; diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 6ecfdae..9733760 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -15,7 +15,7 @@ MODULE_DESCRIPTION("arptables filter table"); #define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | \ (1 << NF_ARP_FORWARD)) -static struct +static const struct { struct arpt_replace repl; struct arpt_standard entries[3]; @@ -45,7 +45,7 @@ static struct .term = ARPT_ERROR_INIT, }; -static struct xt_table packet_filter = { +static const struct xt_table packet_filter = { .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 0b43fd7..cde755d 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -2065,7 +2065,8 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) return ret; } -struct xt_table *ipt_register_table(struct net *net, struct xt_table *table, +struct xt_table *ipt_register_table(struct net *net, + const struct xt_table *table, const struct ipt_replace *repl) { int ret; diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 97dbd94..df566cb 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -53,7 +53,7 @@ static struct .term = IPT_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_filter = { +static const struct xt_table packet_filter = { .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 28647f1..036047f 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -28,7 +28,7 @@ MODULE_DESCRIPTION("iptables mangle table"); (1 << NF_INET_POST_ROUTING)) /* Ouch - five different hooks? Maybe this should be a config option..... -- BC */ -static struct +static const struct { struct ipt_replace repl; struct ipt_standard entries[5]; @@ -64,7 +64,7 @@ static struct .term = IPT_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_mangler = { +static const struct xt_table packet_mangler = { .name = "mangle", .valid_hooks = MANGLE_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 494784c..993edc2 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -9,7 +9,7 @@ #define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT)) -static struct +static const struct { struct ipt_replace repl; struct ipt_standard entries[2]; @@ -36,7 +36,7 @@ static struct .term = IPT_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_raw = { +static const struct xt_table packet_raw = { .name = "raw", .valid_hooks = RAW_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index 8804e1a..99eb76c6 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -27,7 +27,7 @@ MODULE_DESCRIPTION("iptables security table, for MAC rules"); (1 << NF_INET_FORWARD) | \ (1 << NF_INET_LOCAL_OUT) -static struct +static const struct { struct ipt_replace repl; struct ipt_standard entries[3]; @@ -57,7 +57,7 @@ static struct .term = IPT_ERROR_INIT, /* ERROR */ }; -static struct xt_table security_table = { +static const struct xt_table security_table = { .name = "security", .valid_hooks = SECURITY_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 6448a9b..9e81e0d 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -28,7 +28,7 @@ (1 << NF_INET_POST_ROUTING) | \ (1 << NF_INET_LOCAL_OUT)) -static struct +static const struct { struct ipt_replace repl; struct ipt_standard entries[3]; @@ -58,7 +58,7 @@ static struct .term = IPT_ERROR_INIT, /* ERROR */ }; -static struct xt_table nat_table = { +static const struct xt_table nat_table = { .name = "nat", .valid_hooks = NAT_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index a5d0c27..cc9f8ef 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2100,7 +2100,8 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) return ret; } -struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table, +struct xt_table *ip6t_register_table(struct net *net, + const struct xt_table *table, const struct ip6t_replace *repl) { int ret; diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 0a3ae48..6f4383a 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -51,7 +51,7 @@ static struct .term = IP6T_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_filter = { +static const struct xt_table packet_filter = { .name = "filter", .valid_hooks = FILTER_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 0f49e00..0ad9143 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -21,7 +21,7 @@ MODULE_DESCRIPTION("ip6tables mangle table"); (1 << NF_INET_LOCAL_OUT) | \ (1 << NF_INET_POST_ROUTING)) -static struct +static const struct { struct ip6t_replace repl; struct ip6t_standard entries[5]; @@ -57,7 +57,7 @@ static struct .term = IP6T_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_mangler = { +static const struct xt_table packet_mangler = { .name = "mangle", .valid_hooks = MANGLE_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 679865e..ed1a118 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -8,7 +8,7 @@ #define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT)) -static struct +static const struct { struct ip6t_replace repl; struct ip6t_standard entries[2]; @@ -35,7 +35,7 @@ static struct .term = IP6T_ERROR_INIT, /* ERROR */ }; -static struct xt_table packet_raw = { +static const struct xt_table packet_raw = { .name = "raw", .valid_hooks = RAW_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 822afab..41b444c 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -26,7 +26,7 @@ MODULE_DESCRIPTION("ip6tables security table, for MAC rules"); (1 << NF_INET_FORWARD) | \ (1 << NF_INET_LOCAL_OUT) -static struct +static const struct { struct ip6t_replace repl; struct ip6t_standard entries[3]; @@ -56,7 +56,7 @@ static struct .term = IP6T_ERROR_INIT, /* ERROR */ }; -static struct xt_table security_table = { +static const struct xt_table security_table = { .name = "security", .valid_hooks = SECURITY_VALID_HOOKS, .me = THIS_MODULE, diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 025d1a0..a6ac83a 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -736,16 +736,17 @@ xt_replace_table(struct xt_table *table, } EXPORT_SYMBOL_GPL(xt_replace_table); -struct xt_table *xt_register_table(struct net *net, struct xt_table *table, +struct xt_table *xt_register_table(struct net *net, + const struct xt_table *input_table, struct xt_table_info *bootstrap, struct xt_table_info *newinfo) { int ret; struct xt_table_info *private; - struct xt_table *t; + struct xt_table *t, *table; /* Don't add one object to multiple lists. */ - table = kmemdup(table, sizeof(struct xt_table), GFP_KERNEL); + table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL); if (!table) { ret = -ENOMEM; goto out; -- cgit v1.1 From cce5a5c3029f731b5ea17a8a9a953ff742abf0d6 Mon Sep 17 00:00:00 2001 From: Maximilian Engelhardt Date: Mon, 24 Aug 2009 19:24:54 +0200 Subject: netfilter: nf_nat: fix inverted logic for persistent NAT mappings Kernel 2.6.30 introduced a patch [1] for the persistent option in the netfilter SNAT target. This is exactly what we need here so I had a quick look at the code and noticed that the patch is wrong. The logic is simply inverted. The patch below fixes this. Also note that because of this the default behavior of the SNAT target has changed since kernel 2.6.30 as it now ignores the destination IP in choosing the source IP for nating (which should only be the case if the persistent option is set). [1] http://git.eu.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=98d500d66cb7940747b424b245fc6a51ecfbf005 Signed-off-by: Maximilian Engelhardt Signed-off-by: Patrick McHardy --- net/ipv4/netfilter/nf_nat_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 3229e0a..b6ddd56 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -212,7 +212,7 @@ find_best_ips_proto(struct nf_conntrack_tuple *tuple, maxip = ntohl(range->max_ip); j = jhash_2words((__force u32)tuple->src.u3.ip, range->flags & IP_NAT_RANGE_PERSISTENT ? - (__force u32)tuple->dst.u3.ip : 0, 0); + 0 : (__force u32)tuple->dst.u3.ip, 0); j = ((u64)j * (maxip - minip + 1)) >> 32; *var_ipp = htonl(minip + j); } -- cgit v1.1 From f3abc9b963e004b8c96cd7fbee6fd905f2bfd620 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 24 Aug 2009 19:35:38 +0200 Subject: netfilter: bridge: refcount fix commit f216f082b2b37c4943f1e7c393e2786648d48f6f ([NETFILTER]: bridge netfilter: deal with martians correctly) added a refcount leak on in_dev. Instead of using in_dev_get(), we can use __in_dev_get_rcu(), as netfilter hooks are running under rcu_read_lock(), as pointed by Patrick. Signed-off-by: Eric Dumazet Signed-off-by: Patrick McHardy --- net/bridge/br_netfilter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 4fde742..907a82e 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -359,7 +359,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) }, .proto = 0, }; - struct in_device *in_dev = in_dev_get(dev); + struct in_device *in_dev = __in_dev_get_rcu(dev); /* If err equals -EHOSTUNREACH the error is due to a * martian destination or due to the fact that -- cgit v1.1 From cbe86b98a6aceefe943ada1471eb52fd9ac4c504 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 24 Aug 2009 16:32:50 -0700 Subject: Bluetooth: Add missing selection of CONFIG_CRC16 for L2CAP layer Fix net/bluetooth/l2cap.c build errors: l2cap.c:(.text+0x126035): undefined reference to `crc16' l2cap.c:(.text+0x126323): undefined reference to `crc16' l2cap.c:(.text+0x12668e): undefined reference to `crc16' l2cap.c:(.text+0x12683b): undefined reference to `crc16' l2cap.c:(.text+0x126956): undefined reference to `crc16' net/built-in.o:l2cap.c:(.text+0x129041): more undefined references to `crc16' follow Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Marcel Holtmann --- net/bluetooth/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 59fdb1d..ed37168 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -34,6 +34,7 @@ menuconfig BT config BT_L2CAP tristate "L2CAP protocol support" depends on BT + select CRC16 help L2CAP (Logical Link Control and Adaptation Protocol) provides connection oriented and connection-less data transport. L2CAP -- cgit v1.1 From 74f7a6552c8d76ffc5e11eb8d9d6c07238b9ae77 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 25 Aug 2009 15:33:08 +0200 Subject: netfilter: nf_conntrack: log packets dropped by helpers Log packets dropped by helpers using the netfilter logging API. This is useful in combination with nfnetlink_log to analyze those packets in userspace for debugging. Signed-off-by: Patrick McHardy --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 +++++- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 9ac2fdc..aa95bb8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -26,6 +26,7 @@ #include #include #include +#include int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, struct nf_conn *ct, @@ -113,8 +114,11 @@ static unsigned int ipv4_confirm(unsigned int hooknum, ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ct, ctinfo); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL, + "nf_ct_%s: dropping packet", helper->name); return ret; + } if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { typeof(nf_nat_seq_adjust_hook) seq_adjust; diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index a7f4cd6..5f2ec20 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -27,6 +27,7 @@ #include #include #include +#include static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple) @@ -176,8 +177,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum, } ret = helper->help(skb, protoff, ct, ctinfo); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL, + "nf_ct_%s: dropping packet", helper->name); return ret; + } out: /* We've seen it coming out the other side: confirm it */ return nf_conntrack_confirm(skb); -- cgit v1.1 From 3a6c2b419b7768703cfb2cabdb894517c5065e33 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 25 Aug 2009 16:07:40 +0200 Subject: netlink: constify nlmsghdr arguments Consitfy nlmsghdr arguments to a couple of functions as preparation for the next patch, which will constify the netlink message data in all nfnetlink users. Signed-off-by: Patrick McHardy --- net/netlink/af_netlink.c | 2 +- net/sched/act_api.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index da3163d..d0ff382 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1705,7 +1705,7 @@ errout: } int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, - struct nlmsghdr *nlh, + const struct nlmsghdr *nlh, int (*dump)(struct sk_buff *skb, struct netlink_callback *), int (*done)(struct netlink_callback *)) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 9d03cc3..2dfb3e7 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1011,7 +1011,7 @@ replay: } static struct nlattr * -find_dump_kind(struct nlmsghdr *n) +find_dump_kind(const struct nlmsghdr *n) { struct nlattr *tb1, *tb2[TCA_ACT_MAX+1]; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; -- cgit v1.1 From 3993832464dd4e14a4c926583a11f0fa92c1f0f0 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 25 Aug 2009 16:07:58 +0200 Subject: netfilter: nfnetlink: constify message attributes and headers Signed-off-by: Patrick McHardy --- net/ipv4/netfilter/nf_nat_core.c | 6 ++-- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 54 ++++++++++++++++++++++-------------- net/netfilter/nfnetlink.c | 2 +- net/netfilter/nfnetlink_log.c | 6 ++-- net/netfilter/nfnetlink_queue.c | 9 ++++-- net/netfilter/xt_osf.c | 6 ++-- 7 files changed, 52 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index b6ddd56..68afc6e 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -620,7 +620,7 @@ static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { }; static int -nfnetlink_parse_nat(struct nlattr *nat, +nfnetlink_parse_nat(const struct nlattr *nat, const struct nf_conn *ct, struct nf_nat_range *range) { struct nlattr *tb[CTA_NAT_MAX+1]; @@ -656,7 +656,7 @@ nfnetlink_parse_nat(struct nlattr *nat, static int nfnetlink_parse_nat_setup(struct nf_conn *ct, enum nf_nat_manip_type manip, - struct nlattr *attr) + const struct nlattr *attr) { struct nf_nat_range range; @@ -671,7 +671,7 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct, static int nfnetlink_parse_nat_setup(struct nf_conn *ct, enum nf_nat_manip_type manip, - struct nlattr *attr) + const struct nlattr *attr) { return -EOPNOTSUPP; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b5869b9..565c3a8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -47,7 +47,7 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, enum nf_nat_manip_type manip, - struct nlattr *attr) __read_mostly; + const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); DEFINE_SPINLOCK(nf_conntrack_lock); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 49479d1..59d8064 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -704,7 +704,8 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr, } static int -ctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple, +ctnetlink_parse_tuple(const struct nlattr * const cda[], + struct nf_conntrack_tuple *tuple, enum ctattr_tuple type, u_int8_t l3num) { struct nlattr *tb[CTA_TUPLE_MAX+1]; @@ -740,7 +741,7 @@ ctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple, } static inline int -ctnetlink_parse_help(struct nlattr *attr, char **helper_name) +ctnetlink_parse_help(const struct nlattr *attr, char **helper_name) { struct nlattr *tb[CTA_HELP_MAX+1]; @@ -764,7 +765,8 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { static int ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple tuple; @@ -823,7 +825,8 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, static int ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple tuple; @@ -884,7 +887,7 @@ out: static int ctnetlink_parse_nat_setup(struct nf_conn *ct, enum nf_nat_manip_type manip, - struct nlattr *attr) + const struct nlattr *attr) { typeof(nfnetlink_parse_nat_setup_hook) parse_nat_setup; @@ -914,7 +917,7 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, #endif static int -ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[]) { unsigned long d; unsigned int status = ntohl(nla_get_be32(cda[CTA_STATUS])); @@ -940,7 +943,7 @@ ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) } static int -ctnetlink_change_nat(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_nat(struct nf_conn *ct, const struct nlattr * const cda[]) { #ifdef CONFIG_NF_NAT_NEEDED int ret; @@ -966,7 +969,7 @@ ctnetlink_change_nat(struct nf_conn *ct, struct nlattr *cda[]) } static inline int -ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) { struct nf_conntrack_helper *helper; struct nf_conn_help *help = nfct_help(ct); @@ -1028,7 +1031,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) } static inline int -ctnetlink_change_timeout(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_timeout(struct nf_conn *ct, const struct nlattr * const cda[]) { u_int32_t timeout = ntohl(nla_get_be32(cda[CTA_TIMEOUT])); @@ -1042,9 +1045,10 @@ ctnetlink_change_timeout(struct nf_conn *ct, struct nlattr *cda[]) } static inline int -ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]) { - struct nlattr *tb[CTA_PROTOINFO_MAX+1], *attr = cda[CTA_PROTOINFO]; + const struct nlattr *attr = cda[CTA_PROTOINFO]; + struct nlattr *tb[CTA_PROTOINFO_MAX+1]; struct nf_conntrack_l4proto *l4proto; int err = 0; @@ -1061,7 +1065,7 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[]) #ifdef CONFIG_NF_NAT_NEEDED static inline int -change_nat_seq_adj(struct nf_nat_seq *natseq, struct nlattr *attr) +change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr) { struct nlattr *cda[CTA_NAT_SEQ_MAX+1]; @@ -1089,7 +1093,8 @@ change_nat_seq_adj(struct nf_nat_seq *natseq, struct nlattr *attr) } static int -ctnetlink_change_nat_seq_adj(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_nat_seq_adj(struct nf_conn *ct, + const struct nlattr * const cda[]) { int ret = 0; struct nf_conn_nat *nat = nfct_nat(ct); @@ -1120,7 +1125,8 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, struct nlattr *cda[]) #endif static int -ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) +ctnetlink_change_conntrack(struct nf_conn *ct, + const struct nlattr * const cda[]) { int err; @@ -1169,7 +1175,7 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) } static struct nf_conn * -ctnetlink_create_conntrack(struct nlattr *cda[], +ctnetlink_create_conntrack(const struct nlattr * const cda[], struct nf_conntrack_tuple *otuple, struct nf_conntrack_tuple *rtuple, u8 u3) @@ -1304,7 +1310,8 @@ err1: static int ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_tuple otuple, rtuple; struct nf_conntrack_tuple_hash *h = NULL; @@ -1629,7 +1636,8 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { static int ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_tuple tuple; struct nf_conntrack_expect *exp; @@ -1689,7 +1697,8 @@ out: static int ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_expect *exp; struct nf_conntrack_tuple tuple; @@ -1767,13 +1776,15 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, return 0; } static int -ctnetlink_change_expect(struct nf_conntrack_expect *x, struct nlattr *cda[]) +ctnetlink_change_expect(struct nf_conntrack_expect *x, + const struct nlattr * const cda[]) { return -EOPNOTSUPP; } static int -ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3, u32 pid, int report) +ctnetlink_create_expect(const struct nlattr * const cda[], u_int8_t u3, + u32 pid, int report) { struct nf_conntrack_tuple tuple, mask, master_tuple; struct nf_conntrack_tuple_hash *h = NULL; @@ -1831,7 +1842,8 @@ out: static int ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *cda[]) + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) { struct nf_conntrack_tuple tuple; struct nf_conntrack_expect *exp; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 92761a9..eedc0c1 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -170,7 +170,7 @@ replay: if (err < 0) return err; - err = nc->call(nfnl, skb, nlh, cda); + err = nc->call(nfnl, skb, nlh, (const struct nlattr **)cda); if (err == -EAGAIN) goto replay; return err; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 66a6dd5..f900dc3 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -694,7 +694,8 @@ static struct notifier_block nfulnl_rtnl_notifier = { static int nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *nfqa[]) + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) { return -ENOTSUPP; } @@ -716,7 +717,8 @@ static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = { static int nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *nfula[]) + const struct nlmsghdr *nlh, + const struct nlattr * const nfula[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t group_num = ntohs(nfmsg->res_id); diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 71daa09..7a9dec9 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -608,7 +608,8 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { static int nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *nfqa[]) + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); @@ -670,7 +671,8 @@ err_out_unlock: static int nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *nfqa[]) + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) { return -ENOTSUPP; } @@ -687,7 +689,8 @@ static const struct nf_queue_handler nfqh = { static int nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *nfqa[]) + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) { struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 0f482e2..63e1905 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -70,7 +70,8 @@ static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head) } static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *osf_attrs[]) + const struct nlmsghdr *nlh, + const struct nlattr * const osf_attrs[]) { struct xt_osf_user_finger *f; struct xt_osf_finger *kf = NULL, *sf; @@ -112,7 +113,8 @@ static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb, } static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb, - struct nlmsghdr *nlh, struct nlattr *osf_attrs[]) + const struct nlmsghdr *nlh, + const struct nlattr * const osf_attrs[]) { struct xt_osf_user_finger *f; struct xt_osf_finger *sf; -- cgit v1.1 From ca42a613c92d131ff02d5714419d58c36c3459f3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 26 Aug 2009 04:04:01 -0300 Subject: Bluetooth: Acknowledge L2CAP packets when receiving RR-frames (F-bit=1) Implement the Recv ReqSeqAndFBit event when a RR frame with F bit set is received. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index e5847c5..0a36c61 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -3348,9 +3348,13 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str case L2CAP_SUPER_RCV_READY: if (rx_control & L2CAP_CTRL_POLL) { u16 control = L2CAP_CTRL_FINAL; - control |= L2CAP_SUPER_RCV_READY; + control |= L2CAP_SUPER_RCV_READY | + (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT); l2cap_send_sframe(l2cap_pi(sk), control); } else if (rx_control & L2CAP_CTRL_FINAL) { + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + if (!(pi->conn_state & L2CAP_CONN_WAIT_F)) break; -- cgit v1.1 From 2246b2f1b43f3fbd128e72b129dcbbd3202cc592 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 26 Aug 2009 04:04:02 -0300 Subject: Bluetooth: Handle L2CAP case when the remote receiver is busy Implement all issues related to RemoteBusy in the RECV state table. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0a36c61..40fbf5c 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1350,7 +1350,8 @@ static int l2cap_ertm_send(struct sock *sk) if (pi->conn_state & L2CAP_CONN_WAIT_F) return 0; - while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { + while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk)) + && !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) { tx_skb = skb_clone(skb, GFP_ATOMIC); if (pi->remote_max_tx && @@ -3351,7 +3352,10 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str control |= L2CAP_SUPER_RCV_READY | (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT); l2cap_send_sframe(l2cap_pi(sk), control); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + } else if (rx_control & L2CAP_CTRL_FINAL) { + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); @@ -3366,13 +3370,19 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str } else { pi->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(sk); - if (pi->unacked_frames > 0) + + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) + && (pi->unacked_frames > 0)) __mod_retrans_timer(); + l2cap_ertm_send(sk); + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; } break; case L2CAP_SUPER_REJECT: + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + pi->expected_ack_seq = __get_reqseq(rx_control); l2cap_drop_acked_frames(sk); @@ -3384,6 +3394,8 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str break; case L2CAP_SUPER_SELECT_REJECT: + pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + if (rx_control & L2CAP_CTRL_POLL) { l2cap_retransmit_frame(sk, tx_seq); pi->expected_ack_seq = tx_seq; @@ -3410,6 +3422,15 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str break; case L2CAP_SUPER_RCV_NOT_READY: + pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; + pi->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(sk); + + del_timer(&l2cap_pi(sk)->retrans_timer); + if (rx_control & L2CAP_CTRL_POLL) { + u16 control = L2CAP_CTRL_FINAL | L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(l2cap_pi(sk), control); + } break; } -- cgit v1.1 From 7e7430908c3ccaf71f0851050c8ccaf9ecfb3b56 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 26 Aug 2009 04:04:03 -0300 Subject: Bluetooth: Add support for L2CAP 'Send RRorRNR' action When called, 'Send RRorRNR' should send a RNR frame if local device is busy or a RR frame otherwise. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 40fbf5c..b030125 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -366,6 +366,16 @@ static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) return hci_send_acl(pi->conn->hcon, skb, 0); } +static inline int l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) +{ + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) + control |= L2CAP_SUPER_RCV_NOT_READY; + else + control |= L2CAP_SUPER_RCV_READY; + + return l2cap_send_sframe(pi, control); +} + static void l2cap_do_start(struct sock *sk) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; @@ -1202,8 +1212,7 @@ static void l2cap_monitor_timeout(unsigned long arg) __mod_monitor_timer(); control = L2CAP_CTRL_POLL; - control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(l2cap_pi(sk), control); + l2cap_send_rr_or_rnr(l2cap_pi(sk), control); bh_unlock_sock(sk); } @@ -1219,8 +1228,7 @@ static void l2cap_retrans_timeout(unsigned long arg) l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; control = L2CAP_CTRL_POLL; - control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(l2cap_pi(sk), control); + l2cap_send_rr_or_rnr(l2cap_pi(sk), control); bh_unlock_sock(sk); } @@ -3428,8 +3436,8 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str del_timer(&l2cap_pi(sk)->retrans_timer); if (rx_control & L2CAP_CTRL_POLL) { - u16 control = L2CAP_CTRL_FINAL | L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(l2cap_pi(sk), control); + u16 control = L2CAP_CTRL_FINAL; + l2cap_send_rr_or_rnr(l2cap_pi(sk), control); } break; } -- cgit v1.1 From 9e03fdfd05e733e1136d431973625b174029c5e6 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 20 Aug 2009 09:21:45 -0700 Subject: mac80211: Update mesh config IE to 11s draft 3.02 The mesh config information element has changed significantly since draft 1.08 This patch brings it up to date. Thanks to Sam Leffler and Rui Paulo for identifying this. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 4 ++++ net/mac80211/mesh.c | 49 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 93e618a..455cc7a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -367,6 +367,10 @@ struct ieee80211_if_mesh { u8 mesh_pm_id[4]; /* Congestion Control Mode Identifier */ u8 mesh_cc_id[4]; + /* Synchronization Protocol Identifier */ + u8 mesh_sp_id[4]; + /* Authentication Protocol Identifier */ + u8 mesh_auth_id[4]; /* Local mesh Destination Sequence Number */ u32 dsn; /* Last used PREQ ID */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 3185e18..f7364e5 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -18,8 +18,11 @@ #define PP_OFFSET 1 /* Path Selection Protocol */ #define PM_OFFSET 5 /* Path Selection Metric */ #define CC_OFFSET 9 /* Congestion Control Mode */ -#define CAPAB_OFFSET 17 -#define ACCEPT_PLINKS 0x80 +#define SP_OFFSET 13 /* Synchronization Protocol */ +#define AUTH_OFFSET 17 /* Authentication Protocol */ +#define CAPAB_OFFSET 22 +#define CAPAB_ACCEPT_PLINKS 0x80 +#define CAPAB_FORWARDING 0x10 #define TMR_RUNNING_HK 0 #define TMR_RUNNING_MP 1 @@ -84,7 +87,9 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && memcmp(ifmsh->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 && memcmp(ifmsh->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 && - memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0) + memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0 && + memcmp(ifmsh->mesh_sp_id, ie->mesh_config + SP_OFFSET, 4) == 0 && + memcmp(ifmsh->mesh_auth_id, ie->mesh_config + AUTH_OFFSET, 4) == 0) return true; return false; @@ -97,7 +102,7 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat */ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) { - return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0; + return (*(ie->mesh_config + CAPAB_OFFSET) & CAPAB_ACCEPT_PLINKS) != 0; } /** @@ -123,11 +128,18 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) void mesh_ids_set_default(struct ieee80211_if_mesh *sta) { - u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff}; - - memcpy(sta->mesh_pp_id, def_id, 4); - memcpy(sta->mesh_pm_id, def_id, 4); - memcpy(sta->mesh_cc_id, def_id, 4); + u8 oui[3] = {0x00, 0x0F, 0xAC}; + + memcpy(sta->mesh_pp_id, oui, sizeof(oui)); + memcpy(sta->mesh_pm_id, oui, sizeof(oui)); + memcpy(sta->mesh_cc_id, oui, sizeof(oui)); + memcpy(sta->mesh_sp_id, oui, sizeof(oui)); + memcpy(sta->mesh_auth_id, oui, sizeof(oui)); + sta->mesh_pp_id[sizeof(oui)] = 0; + sta->mesh_pm_id[sizeof(oui)] = 0; + sta->mesh_cc_id[sizeof(oui)] = 0xff; + sta->mesh_sp_id[sizeof(oui)] = 0xff; + sta->mesh_auth_id[sizeof(oui)] = 0x0; } int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) @@ -245,7 +257,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) if (sdata->u.mesh.mesh_id_len) memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); - pos = skb_put(skb, 21); + pos = skb_put(skb, 2 + IEEE80211_MESH_CONFIG_LEN); *pos++ = WLAN_EID_MESH_CONFIG; *pos++ = IEEE80211_MESH_CONFIG_LEN; /* Version */ @@ -263,15 +275,22 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) memcpy(pos, sdata->u.mesh.mesh_cc_id, 4); pos += 4; - /* Channel precedence: - * Not running simple channel unification protocol - */ - memset(pos, 0x00, 4); + /* Synchronization protocol identifier */ + memcpy(pos, sdata->u.mesh.mesh_sp_id, 4); pos += 4; + /* Authentication Protocol identifier */ + memcpy(pos, sdata->u.mesh.mesh_auth_id, 4); + pos += 4; + + /* Mesh Formation Info */ + memset(pos, 0x00, 1); + pos += 1; + /* Mesh capability */ sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); - *pos++ = sdata->u.mesh.accepting_plinks ? ACCEPT_PLINKS : 0x00; + *pos = CAPAB_FORWARDING; + *pos++ |= sdata->u.mesh.accepting_plinks ? CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; return; -- cgit v1.1 From 84f6a01ce05fa671f7745b6e041e698a2d1f1341 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 20 Aug 2009 20:02:20 +0200 Subject: mac80211: fix configure_filter invocation after stop Since configure_filter can sleep now, any multicast configuration needed to be postponed to a work struct. This, however, lead to a problem that we could queue the work, stop the device and then afterwards invoke configure_filter which may lead to driver hangs and is a bug. To fix this, we can just cancel the filter work since it's unnecessary to do after stopping the hw. Since there are various places that call drv_stop, and two of them do very similar things, the code for them can be put into a shared function at the same time. Signed-off-by: Johannes Berg Reported-by: Lennert Buytenhek Tested-by: Lennert Buytenhek Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 6 +----- net/mac80211/pm.c | 13 ++----------- net/mac80211/util.c | 10 ++++++++++ 4 files changed, 14 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 455cc7a..fa930e0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1086,6 +1086,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, /* Suspend/resume and hw reconfiguration */ int ieee80211_reconfig(struct ieee80211_local *local); +void ieee80211_stop_device(struct ieee80211_local *local); #ifdef CONFIG_PM int __ieee80211_suspend(struct ieee80211_hw *hw); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b161301..5940e69 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -552,11 +552,7 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { - drv_stop(local); - - ieee80211_led_radio(local, false); - - flush_workqueue(local->workqueue); + ieee80211_stop_device(local); tasklet_disable(&local->tx_pending_tasklet); tasklet_disable(&local->tasklet); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index a5d2f1f..e535f1c 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -107,17 +107,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) } /* stop hardware - this must stop RX */ - if (local->open_count) { - ieee80211_led_radio(local, false); - drv_stop(local); - } - - /* - * flush again, in case driver queued work -- it - * shouldn't be doing (or cancel everything in the - * stop callback) that but better safe than sorry. - */ - flush_workqueue(local->workqueue); + if (local->open_count) + ieee80211_stop_device(local); local->suspended = true; /* need suspended to be visible before quiescing is false */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5eb3063..dd65643 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1007,6 +1007,16 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, return supp_rates; } +void ieee80211_stop_device(struct ieee80211_local *local) +{ + ieee80211_led_radio(local, false); + + cancel_work_sync(&local->reconfig_filter); + drv_stop(local); + + flush_workqueue(local->workqueue); +} + int ieee80211_reconfig(struct ieee80211_local *local) { struct ieee80211_hw *hw = &local->hw; -- cgit v1.1 From 01a0ac417ce9b4f1216a266f2fd454cffefc5aee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 20 Aug 2009 21:36:16 +0200 Subject: cfg80211: check lost scans later, fix bug When we lose a scan, cfg80211 tries to clean up after the driver. However, it currently does this too early, it does this in GOING_DOWN already instead of DOWN, so it may happen with mac80211. Besides fixing this, also make it more robust by leaking the scan request so if the driver later actually finishes the scan, it won't crash. Also check in ___cfg80211_scan_done whether a scan request is still pending and exit if not. Reported-by: Felix Fietkau Signed-off-by: Johannes Berg Tested-by: Felix Fietkau Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +++- net/wireless/core.h | 2 +- net/wireless/scan.c | 21 ++++++++++++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 154e1e2..9b157ca 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -664,7 +664,7 @@ static void wdev_cleanup_work(struct work_struct *work) if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); + ___cfg80211_scan_done(rdev, true); } cfg80211_unlock_rdev(rdev); @@ -755,6 +755,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + break; + case NETDEV_DOWN: dev_hold(dev); schedule_work(&wdev->cleanup_work); break; diff --git a/net/wireless/core.h b/net/wireless/core.h index f565432..68eaf34 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -370,7 +370,7 @@ void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); struct ieee80211_channel * diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fe575a2..7043de6 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -18,7 +18,7 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { struct cfg80211_scan_request *request; struct net_device *dev; @@ -26,8 +26,13 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) union iwreq_data wrqu; #endif + ASSERT_RDEV_LOCK(rdev); + request = rdev->scan_req; + if (!request) + return; + dev = request->dev; /* @@ -53,7 +58,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) dev_put(dev); rdev->scan_req = NULL; - kfree(request); + + /* + * OK. If this is invoked with "leak" then we can't + * free this ... but we've cleaned it up anyway. The + * driver failed to call the scan_done callback, so + * all bets are off, it might still be trying to use + * the scan request or not ... if it accesses the dev + * in there (it shouldn't anyway) then it may crash. + */ + if (!leak) + kfree(request); } void __cfg80211_scan_done(struct work_struct *wk) @@ -64,7 +79,7 @@ void __cfg80211_scan_done(struct work_struct *wk) scan_done_wk); cfg80211_lock_rdev(rdev); - ___cfg80211_scan_done(rdev); + ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev); } -- cgit v1.1 From 103bf9f7d35849bce52ad412e4da5063b0716969 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Thu, 20 Aug 2009 16:34:15 -0400 Subject: mac80211: remove ieee80211_rx namespace hack With the libipw naming scheme change, it is no longer necessary for mac80211 to avoid the ieee80211_rx name clash. Reported-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7065fd7..dff2239 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2440,7 +2440,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) +void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate = NULL; @@ -2523,7 +2523,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) rcu_read_unlock(); } -EXPORT_SYMBOL(__ieee80211_rx); +EXPORT_SYMBOL(ieee80211_rx); /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ -- cgit v1.1 From f7969969f416e593bcc7dc24abf3f9fd6c27136d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2009 12:23:49 +0200 Subject: cfg80211: make spurious warnings less likely, configurable Bob reported that he got warnings in IBSS mode about the ssid_len being zero on a joined event, but only when kmemcheck was enabled. This appears to be due to a race condition between drivers and userspace, when the driver reports joined but the user in the meantime decided to leave the IBSS again, the warning would trigger. This was made more likely by kmemcheck delaying the code that does the check and sends the event. So first, make the warning trigger closer to the driver, which means it's not locked, but since only the warning depends on it that's ok. And secondly, users will not want to have spurious warnings at all, so make those that are known to be racy in such a way configurable. Reported-by: Bob Copeland Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/Kconfig | 17 +++++++++++++++++ net/wireless/core.h | 11 +++++++++++ net/wireless/ibss.c | 4 +++- net/wireless/sme.c | 12 +++++++++--- 4 files changed, 40 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index c6031d5..aea7e68 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -17,6 +17,23 @@ config NL80211_TESTMODE Say N. +config CFG80211_DEVELOPER_WARNINGS + bool "enable developer warnings" + depends on CFG80211 + default n + help + This option enables some additional warnings that help + cfg80211 developers and driver developers, but that can + trigger due to races with userspace. + + For example, when a driver reports that it was disconnected + from the AP, but the user disconnects manually at the same + time, the warning might trigger spuriously due to races. + + Say Y only if you are developing cfg80211 or a driver based + on it (or mac80211). + + config CFG80211_REG_DEBUG bool "cfg80211 regulatory debugging" depends on CFG80211 diff --git a/net/wireless/core.h b/net/wireless/core.h index 68eaf34..d262d42 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -380,4 +380,15 @@ int rdev_set_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *for_wdev, int freq, enum nl80211_channel_type channel_type); +#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS +#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) +#else +/* + * Trick to enable using it as a condition, + * and also not give a warning when it's + * not used that way. + */ +#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) +#endif + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 42840a0..c883389 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -22,7 +22,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return; - if (WARN_ON(!wdev->ssid_len)) + if (!wdev->ssid_len) return; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, @@ -58,6 +58,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(!wdev->ssid_len); + ev = kzalloc(sizeof(*ev), gfp); if (!ev) return; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 4a8289f..6830788 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -351,7 +351,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + if (wdev->sme_state != CFG80211_SME_CONNECTING) return; nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, @@ -445,6 +445,8 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) return; @@ -481,7 +483,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + if (wdev->sme_state != CFG80211_SME_CONNECTED) return; /* internal error -- how did we get to CONNECTED w/o BSS? */ @@ -540,6 +542,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) return; @@ -575,7 +579,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + if (wdev->sme_state != CFG80211_SME_CONNECTED) return; if (wdev->current_bss) { @@ -639,6 +643,8 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, struct cfg80211_event *ev; unsigned long flags; + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); + ev = kzalloc(sizeof(*ev) + ie_len, gfp); if (!ev) return; -- cgit v1.1 From 3d54d25515838543e56889aa7e48f40d00719368 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2009 14:51:05 +0200 Subject: cfg80211: clean up properly on interface type change When the interface type changes while connected, and the driver does not require the interface to be down for a type change, it is currently possible to get very strange results unless the driver takes special care, which it shouldn't have to. To fix this, take care to disconnect/leave IBSS when changing the interface type -- even if the driver may fail the call. Also process all events that may be pending to avoid running into a situation where an event is reported but only processed after the type has already changed, which would lead to missing events and warnings. A side effect of this is that you will have disconnected or left the IBSS even if the mode change ultimately fails, but since the intention was to change it and thus leave or disconnect, this is not a problem. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 54 +---------------------- net/wireless/core.h | 4 ++ net/wireless/nl80211.c | 16 +------ net/wireless/util.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ net/wireless/wext-compat.c | 16 ++----- 5 files changed, 117 insertions(+), 81 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 9b157ca..45b2be3 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -294,69 +294,17 @@ static void cfg80211_rfkill_sync_work(struct work_struct *work) cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); } -static void cfg80211_process_events(struct wireless_dev *wdev) -{ - struct cfg80211_event *ev; - unsigned long flags; - - spin_lock_irqsave(&wdev->event_lock, flags); - while (!list_empty(&wdev->event_list)) { - ev = list_first_entry(&wdev->event_list, - struct cfg80211_event, list); - list_del(&ev->list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - - wdev_lock(wdev); - switch (ev->type) { - case EVENT_CONNECT_RESULT: - __cfg80211_connect_result( - wdev->netdev, is_zero_ether_addr(ev->cr.bssid) ? - NULL : ev->cr.bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - NULL); - break; - case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bssid, - ev->rm.req_ie, ev->rm.req_ie_len, - ev->rm.resp_ie, ev->rm.resp_ie_len); - break; - case EVENT_DISCONNECTED: - __cfg80211_disconnected(wdev->netdev, - ev->dc.ie, ev->dc.ie_len, - ev->dc.reason, true); - break; - case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); - break; - } - wdev_unlock(wdev); - - kfree(ev); - - spin_lock_irqsave(&wdev->event_lock, flags); - } - spin_unlock_irqrestore(&wdev->event_lock, flags); -} - static void cfg80211_event_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; rdev = container_of(work, struct cfg80211_registered_device, event_work); rtnl_lock(); cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) - cfg80211_process_events(wdev); - - mutex_unlock(&rdev->devlist_mtx); + cfg80211_process_rdev_events(rdev); cfg80211_unlock_rdev(rdev); rtnl_unlock(); } diff --git a/net/wireless/core.h b/net/wireless/core.h index d262d42..2a33d8b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -372,6 +372,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params); +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); struct ieee80211_channel * rdev_fixed_channel(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a8aaade..71bfc04 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -977,12 +977,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } } - if (!rdev->ops->change_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << ntype))) { - err = -EOPNOTSUPP; - goto unlock; - } - if (info->attrs[NL80211_ATTR_MESH_ID]) { if (ntype != NL80211_IFTYPE_MESH_POINT) { err = -EINVAL; @@ -1008,18 +1002,10 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, - ntype, flags, ¶ms); + err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); else err = 0; - WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - - if (!err && (ntype != otype)) { - if (otype == NL80211_IFTYPE_ADHOC) - cfg80211_clear_ibss(dev, false); - } - unlock: dev_put(dev); cfg80211_unlock_rdev(rdev); diff --git a/net/wireless/util.c b/net/wireless/util.c index 693275a..3fc2df8 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -574,3 +574,111 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) kfree(wdev->connect_keys); wdev->connect_keys = NULL; } + +static void cfg80211_process_wdev_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + const u8 *bssid = NULL; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + if (!is_zero_ether_addr(ev->cr.bssid)) + bssid = ev->cr.bssid; + __cfg80211_connect_result( + wdev->netdev, bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS, + NULL); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bssid, + ev->rm.req_ie, ev->rm.req_ie_len, + ev->rm.resp_ie, ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); + + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + ASSERT_RDEV_LOCK(rdev); + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_wdev_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); +} + +int cfg80211_change_iface(struct cfg80211_registered_device *rdev, + struct net_device *dev, enum nl80211_iftype ntype, + u32 *flags, struct vif_params *params) +{ + int err; + enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; + + ASSERT_RDEV_LOCK(rdev); + + /* don't support changing VLANs, you just re-create them */ + if (otype == NL80211_IFTYPE_AP_VLAN) + return -EOPNOTSUPP; + + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) + return -EOPNOTSUPP; + + if (ntype != otype) { + switch (otype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, false); + break; + case NL80211_IFTYPE_STATION: + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + break; + case NL80211_IFTYPE_MESH_POINT: + /* mesh should be handled? */ + break; + default: + break; + } + + cfg80211_process_rdev_events(rdev); + } + + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, + ntype, flags, params); + + WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); + + return err; +} diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c12029b..429dd06 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -70,18 +70,8 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, enum nl80211_iftype type; int ret; - if (!wdev) - return -EOPNOTSUPP; - rdev = wiphy_to_dev(wdev->wiphy); - if (!rdev->ops->change_virtual_intf) - return -EOPNOTSUPP; - - /* don't support changing VLANs, you just re-create them */ - if (wdev->iftype == NL80211_IFTYPE_AP_VLAN) - return -EOPNOTSUPP; - switch (*mode) { case IW_MODE_INFRA: type = NL80211_IFTYPE_STATION; @@ -104,9 +94,9 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev, type, - NULL, &vifparams); - WARN_ON(!ret && wdev->iftype != type); + cfg80211_lock_rdev(rdev); + ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); + cfg80211_unlock_rdev(rdev); return ret; } -- cgit v1.1 From ea77f12f2cc0f31168f2e0259e65a22202ac4dc2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Aug 2009 14:44:45 +0200 Subject: mac80211: remove tasklet enable/disable Due to the way the tasklets work in mac80211 there's no need to ever disable them. However, we need to clear the pending packets when taking down the last interface because otherwise the tx_pending_tasklet might be queued if the driver mucks with the queues (which it shouldn't). I've had a situation occasionally with ar9170 in which ksoftirq was using 100% CPU time because a disabled tasklet was scheduled, and I think that was due to ar9170 receiving a packet while the tasklet was disabled. That's strange and it really should not do that for other reasons, but there's no need to waste that much CPU time over it, it should just warn instead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/driver-ops.h | 14 +++++++++++++- net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/iface.c | 9 +-------- net/mac80211/main.c | 2 -- net/mac80211/rx.c | 9 +++++++++ 5 files changed, 26 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index d231c93..020a94a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -12,7 +12,11 @@ static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) static inline int drv_start(struct ieee80211_local *local) { - int ret = local->ops->start(&local->hw); + int ret; + + local->started = true; + smp_mb(); + ret = local->ops->start(&local->hw); trace_drv_start(local, ret); return ret; } @@ -21,6 +25,14 @@ static inline void drv_stop(struct ieee80211_local *local) { local->ops->stop(&local->hw); trace_drv_stop(local); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; } static inline int drv_add_interface(struct ieee80211_local *local, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fa930e0..dbd8411 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -667,6 +667,9 @@ struct ieee80211_local { */ bool quiescing; + /* device is started */ + bool started; + int tx_headroom; /* required headroom for hardware/radiotap */ /* Tasklet and skb queue to process calls from IRQ mode. All frames diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5940e69..d134bd7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -277,11 +277,6 @@ static int ieee80211_open(struct net_device *dev) } } - if (local->open_count == 0) { - tasklet_enable(&local->tx_pending_tasklet); - tasklet_enable(&local->tasklet); - } - /* * set_multicast_list will be invoked by the networking core * which will check whether any increments here were done in @@ -552,11 +547,9 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { + ieee80211_clear_tx_pending(local); ieee80211_stop_device(local); - tasklet_disable(&local->tx_pending_tasklet); - tasklet_disable(&local->tasklet); - /* no reconfiguring after stop! */ hw_reconf_flags = 0; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index dd3b081..797f539 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -715,12 +715,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, skb_queue_head_init(&local->pending[i]); tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, (unsigned long)local); - tasklet_disable(&local->tx_pending_tasklet); tasklet_init(&local->tasklet, ieee80211_tasklet_handler, (unsigned long) local); - tasklet_disable(&local->tasklet); skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index dff2239..b98f1af 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2471,6 +2471,15 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) return; } + /* + * The same happens when we're not even started, + * but that's worth a warning. + */ + if (WARN_ON(!local->started)) { + kfree_skb(skb); + return; + } + if (status->flag & RX_FLAG_HT) { /* rate_idx is MCS index */ if (WARN_ON(status->rate_idx < 0 || -- cgit v1.1 From 1c4e9ab3f1cc458306b3b910000371ba4f71c4d2 Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Fri, 21 Aug 2009 16:11:30 +0200 Subject: mac80211: Remove unnused throughput field from minstrel_rate. I noticed that the throughput field of the minstrel_rate struct is never used, so remove it. Signed-off-by: Arnd Hannemann Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel.h | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 869fe0e..38bf4168 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -33,7 +33,6 @@ struct minstrel_rate { /* per-rate throughput */ u32 cur_tp; - u32 throughput; u64 succ_hist; u64 att_hist; -- cgit v1.1 From 0448b5fc032ea76096eb3cfbe3196b3c01b08b86 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 22 Aug 2009 21:15:49 +0200 Subject: nl80211: jump to out_err upon unsupported iftype Jump to out_err when the iftype is not supported. Signed-off-by: Roel Kluin Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 71bfc04..eddab09 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2181,7 +2181,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { err = -EOPNOTSUPP; - goto out; + goto out_err; } while (1) { -- cgit v1.1 From 77a980dc6c4674fc7741d72b9775135669318d8d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Aug 2009 11:46:30 +0200 Subject: mac80211: fix RX skb leaks In mac80211's RX path some of the warnings that warn about drivers passing invalid status values leak the skb, fix that by refactoring the code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/rx.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b98f1af..c01588f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2447,17 +2447,13 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_supported_band *sband; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - if (status->band < 0 || - status->band >= IEEE80211_NUM_BANDS) { - WARN_ON(1); - return; - } + if (WARN_ON(status->band < 0 || + status->band >= IEEE80211_NUM_BANDS)) + goto drop; sband = local->hw.wiphy->bands[status->band]; - if (!sband) { - WARN_ON(1); - return; - } + if (WARN_ON(!sband)) + goto drop; /* * If we're suspending, it is possible although not too likely @@ -2466,25 +2462,21 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) * that might, for example, cause stations to be added or other * driver callbacks be invoked. */ - if (unlikely(local->quiescing || local->suspended)) { - kfree_skb(skb); - return; - } + if (unlikely(local->quiescing || local->suspended)) + goto drop; /* * The same happens when we're not even started, * but that's worth a warning. */ - if (WARN_ON(!local->started)) { - kfree_skb(skb); - return; - } + if (WARN_ON(!local->started)) + goto drop; if (status->flag & RX_FLAG_HT) { /* rate_idx is MCS index */ if (WARN_ON(status->rate_idx < 0 || status->rate_idx >= 76)) - return; + goto drop; /* HT rates are not in the table - use the highest legacy rate * for now since other parts of mac80211 may not yet be fully * MCS aware. */ @@ -2492,7 +2484,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) } else { if (WARN_ON(status->rate_idx < 0 || status->rate_idx >= sband->n_bitrates)) - return; + goto drop; rate = &sband->bitrates[status->rate_idx]; } @@ -2531,6 +2523,10 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) __ieee80211_rx_handle_packet(hw, skb, rate); rcu_read_unlock(); + + return; + drop: + kfree_skb(skb); } EXPORT_SYMBOL(ieee80211_rx); -- cgit v1.1 From eadac6bf95e1945bb64e6f8f22d8509391645e2e Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Mon, 24 Aug 2009 20:51:46 +0200 Subject: mac80211: Fix output of minstrels rc_stats An integer overflow in the minstrel debug code prevented the throughput to be displayed correctly. This patch fixes that, by permutating operations like proposed by Pavel Roskin. Signed-off-by: Arnd Hannemann Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 98f4807..3d72ec5 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -83,7 +83,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) p += sprintf(p, "%3u%s", mr->bitrate / 2, (mr->bitrate & 1 ? ".5" : " ")); - tp = ((mr->cur_tp * 96) / 18000) >> 10; + tp = mr->cur_tp / ((18000 << 10) / 96); prob = mr->cur_prob / 18; eprob = mr->probability / 18; -- cgit v1.1 From 15db0b7fd872b0312033666d3a82e1214a227ec0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 25 Aug 2009 16:33:47 +0200 Subject: mac80211: fix scan cancel on ifdown When an interface is taken down while a scan is pending -- i.e. a scan request was accepted but not yet acted upon due to other work being in progress -- we currently do not properly cancel that scan and end up getting stuck. Fix this by doing better checks when an interface is taken down. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 26 ++------------------------ net/mac80211/scan.c | 9 ++++++--- 2 files changed, 8 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d134bd7..f6005ad 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -497,30 +497,8 @@ static int ieee80211_stop(struct net_device *dev) } /* fall through */ default: - if (local->scan_sdata == sdata) { - if (!local->ops->hw_scan) - cancel_delayed_work_sync(&local->scan_work); - /* - * The software scan can no longer run now, so we can - * clear out the scan_sdata reference. However, the - * hardware scan may still be running. The complete - * function must be prepared to handle a NULL value. - */ - local->scan_sdata = NULL; - /* - * The memory barrier guarantees that another CPU - * that is hardware-scanning will now see the fact - * that this interface is gone. - */ - smp_mb(); - /* - * If software scanning, complete the scan but since - * the scan_sdata is NULL already don't send out a - * scan event to userspace -- the scan is incomplete. - */ - if (test_bit(SCAN_SW_SCANNING, &local->scanning)) - ieee80211_scan_completed(&local->hw, true); - } + if (local->scan_sdata == sdata) + ieee80211_scan_cancel(local); /* * Disable beaconing for AP and mesh, IBSS can't diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 1e04be6..0399011 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -280,6 +280,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (local->scan_req != local->int_scan_req) cfg80211_scan_done(local->scan_req, aborted); local->scan_req = NULL; + local->scan_sdata = NULL; was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); local->scanning = 0; @@ -660,6 +661,7 @@ void ieee80211_scan_work(struct work_struct *work) int rc; local->scan_req = NULL; + local->scan_sdata = NULL; rc = __ieee80211_start_scan(sdata, req); mutex_unlock(&local->scan_mtx); @@ -742,7 +744,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, void ieee80211_scan_cancel(struct ieee80211_local *local) { - bool swscan; + bool abortscan; cancel_delayed_work_sync(&local->scan_work); @@ -751,9 +753,10 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * queued -- mostly at suspend under RTNL. */ mutex_lock(&local->scan_mtx); - swscan = test_bit(SCAN_SW_SCANNING, &local->scanning); + abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) || + (!local->scanning && local->scan_req); mutex_unlock(&local->scan_mtx); - if (swscan) + if (abortscan) ieee80211_scan_completed(&local->hw, true); } -- cgit v1.1 From 5bf6fcc2bbfb90d997f88c969f1ceee35d064a24 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 25 Aug 2009 17:44:28 +0300 Subject: mac80211: Check pending scan request after having processed mgd work When the queued management work items are processed in ieee80211_sta_work() an item could be removed. This could change the anybusy from true to false, so we better check whether we can start a new scan only after having processed the pending work first. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c374d2d..97a278a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2123,25 +2123,9 @@ static void ieee80211_sta_work(struct work_struct *work) } } - list_for_each_entry(wk, &ifmgd->work_list, list) { - if (wk->state != IEEE80211_MGD_STATE_IDLE) { - anybusy = true; - break; - } - } ieee80211_recalc_idle(local); - if (!anybusy) { - mutex_unlock(&ifmgd->mtx); - - if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) - ieee80211_queue_delayed_work(&local->hw, - &local->scan_work, - round_jiffies_relative(0)); - return; - } - list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { if (time_is_after_jiffies(wk->timeout)) { /* @@ -2187,6 +2171,18 @@ static void ieee80211_sta_work(struct work_struct *work) } } + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (wk->state != IEEE80211_MGD_STATE_IDLE) { + anybusy = true; + break; + } + } + if (!anybusy && + test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + round_jiffies_relative(0)); + mutex_unlock(&ifmgd->mtx); list_for_each_entry_safe(wk, tmp, &free_work, list) { -- cgit v1.1 From 648fda7404630ba85ce462ee1279e1bc027ad915 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:07 +0000 Subject: pktgen: minor cleanup A couple of minor functions can be written more compactly. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 19b8c20..bb46313 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -509,7 +509,7 @@ static const struct file_operations pktgen_fops = { static int pktgen_if_show(struct seq_file *seq, void *v) { - struct pktgen_dev *pkt_dev = seq->private; + const struct pktgen_dev *pkt_dev = seq->private; __u64 sa; __u64 stopped; __u64 now = getCurUs(); @@ -1670,7 +1670,7 @@ static const struct file_operations pktgen_if_fops = { static int pktgen_thread_show(struct seq_file *seq, void *v) { struct pktgen_thread *t = seq->private; - struct pktgen_dev *pkt_dev; + const struct pktgen_dev *pkt_dev; BUG_ON(!t); @@ -2120,13 +2120,9 @@ static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev) pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev); } -static inline int f_seen(struct pktgen_dev *pkt_dev, int flow) +static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow) { - - if (pkt_dev->flows[flow].flags & F_INIT) - return 1; - else - return 0; + return !!(pkt_dev->flows[flow].flags & F_INIT); } static inline int f_pick(struct pktgen_dev *pkt_dev) @@ -3101,17 +3097,14 @@ static void pktgen_stop_all_threads_ifs(void) mutex_unlock(&pktgen_thread_lock); } -static int thread_is_running(struct pktgen_thread *t) +static int thread_is_running(const struct pktgen_thread *t) { - struct pktgen_dev *pkt_dev; - int res = 0; + const struct pktgen_dev *pkt_dev; list_for_each_entry(pkt_dev, &t->if_list, list) - if (pkt_dev->running) { - res = 1; - break; - } - return res; + if (pkt_dev->running) + return 1; + return 0; } static int pktgen_wait_thread_run(struct pktgen_thread *t) -- cgit v1.1 From 475ac1e4099a005e1307c416df19f2100b7a838d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:08 +0000 Subject: pktgen: change inlining Don't force inlining where not needed. Gcc does better job of deciding to inline local functions. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index bb46313..f96e9f0 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2438,7 +2438,7 @@ error: return err; } -static inline void free_SAs(struct pktgen_dev *pkt_dev) +static void free_SAs(struct pktgen_dev *pkt_dev) { if (pkt_dev->cflows) { /* let go of the SAs if we have them */ @@ -2453,7 +2453,7 @@ static inline void free_SAs(struct pktgen_dev *pkt_dev) } } -static inline int process_ipsec(struct pktgen_dev *pkt_dev, +static int process_ipsec(struct pktgen_dev *pkt_dev, struct sk_buff *skb, __be16 protocol) { if (pkt_dev->flags & F_IPSEC_ON) { @@ -3029,8 +3029,8 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, return skb; } -static inline struct sk_buff *fill_packet(struct net_device *odev, - struct pktgen_dev *pkt_dev) +static struct sk_buff *fill_packet(struct net_device *odev, + struct pktgen_dev *pkt_dev) { if (pkt_dev->flags & F_IPV6) return fill_packet_ipv6(odev, pkt_dev); @@ -3341,13 +3341,12 @@ static void pktgen_rem_thread(struct pktgen_thread *t) mutex_unlock(&pktgen_thread_lock); } -static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) +static void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = pkt_dev->odev; int (*xmit)(struct sk_buff *, struct net_device *) = odev->netdev_ops->ndo_start_xmit; struct netdev_queue *txq; - __u64 idle_start = 0; u16 queue_map; int ret; @@ -3379,7 +3378,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) if (netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq) || need_resched()) { - idle_start = getCurUs(); + u64 idle_start = getCurUs(); if (!netif_running(odev)) { pktgen_stop_device(pkt_dev); @@ -3475,7 +3474,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { if (atomic_read(&(pkt_dev->skb->users)) != 1) { - idle_start = getCurUs(); + u64 idle_start = getCurUs(); while (atomic_read(&(pkt_dev->skb->users)) != 1) { if (signal_pending(current)) { break; -- cgit v1.1 From 65c5b786a37746302e225223c0d8b760d4c48a8b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:09 +0000 Subject: pktgen: mark read-only/mostly variables Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index f96e9f0..ccbc3a4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -414,7 +414,7 @@ static __u64 getCurUs(void) /* old include end */ -static char version[] __initdata = VERSION; +static const char version[] __initconst = VERSION; static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i); static int pktgen_add_device(struct pktgen_thread *t, const char *ifname); @@ -432,10 +432,10 @@ static unsigned int scan_ip6(const char *s, char ip[16]); static unsigned int fmt_ip6(char *s, const char ip[16]); /* Module parameters, defaults. */ -static int pg_count_d = 1000; /* 1000 pkts by default */ -static int pg_delay_d; -static int pg_clone_skb_d; -static int debug; +static int pg_count_d __read_mostly = 1000; +static int pg_delay_d __read_mostly; +static int pg_clone_skb_d __read_mostly; +static int debug __read_mostly; static DEFINE_MUTEX(pktgen_thread_lock); static LIST_HEAD(pktgen_threads); -- cgit v1.1 From 3bda06a3d7987bfeabb218ac2f17ce22c34f13b3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:10 +0000 Subject: pktgen: stop_device cleanup All the callers were freeing skb after stopping device. Remove unneeded forward decl. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index ccbc3a4..1b1f126 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -424,7 +424,7 @@ static int pktgen_device_event(struct notifier_block *, unsigned long, void *); static void pktgen_run_all_threads(void); static void pktgen_reset_all_threads(void); static void pktgen_stop_all_threads_ifs(void); -static int pktgen_stop_device(struct pktgen_dev *pkt_dev); + static void pktgen_stop(struct pktgen_thread *t); static void pktgen_clear_counters(struct pktgen_dev *pkt_dev); @@ -3221,7 +3221,6 @@ static void show_results(struct pktgen_dev *pkt_dev, int nr_frags) } /* Set stopped-at timer, remove from running list, do counters & statistics */ - static int pktgen_stop_device(struct pktgen_dev *pkt_dev) { int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1; @@ -3232,6 +3231,8 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev) return -EINVAL; } + kfree_skb(pkt_dev->skb); + pkt_dev->skb = NULL; pkt_dev->stopped_at = getCurUs(); pkt_dev->running = 0; @@ -3268,9 +3269,6 @@ static void pktgen_stop(struct pktgen_thread *t) list_for_each_entry(pkt_dev, &t->if_list, list) { pktgen_stop_device(pkt_dev); - kfree_skb(pkt_dev->skb); - - pkt_dev->skb = NULL; } if_unlock(t); @@ -3382,8 +3380,6 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) if (!netif_running(odev)) { pktgen_stop_device(pkt_dev); - kfree_skb(pkt_dev->skb); - pkt_dev->skb = NULL; goto out; } if (need_resched()) @@ -3486,8 +3482,6 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) /* Done with this */ pktgen_stop_device(pkt_dev); - kfree_skb(pkt_dev->skb); - pkt_dev->skb = NULL; } out:; } -- cgit v1.1 From 3791decb5aa0202d2a2473d6cf4947d98e846c7a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:11 +0000 Subject: pktgen: xmit logic reorganization Do some reorganization of transmit logic path: * move transmit queue full idle to separate routine * add a cpu_relax() * eliminate some of the uneeded goto's * if queue is still stopped, go back to main thread loop. * don't give up transmitting if quantum is exhausted (be greedy) Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 55 ++++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 1b1f126..89fd232 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3339,6 +3339,18 @@ static void pktgen_rem_thread(struct pktgen_thread *t) mutex_unlock(&pktgen_thread_lock); } +static void idle(struct pktgen_dev *pkt_dev) +{ + u64 idle_start = getCurUs(); + + if (need_resched()) + schedule(); + else + cpu_relax(); + + pkt_dev->idle_acc += getCurUs() - idle_start; +} + static void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = pkt_dev->odev; @@ -3361,7 +3373,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) if (pkt_dev->delay_us == 0x7FFFFFFF) { pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us; pkt_dev->next_tx_ns = pkt_dev->delay_ns; - goto out; + return; } } @@ -3373,26 +3385,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) } txq = netdev_get_tx_queue(odev, queue_map); - if (netif_tx_queue_stopped(txq) || - netif_tx_queue_frozen(txq) || - need_resched()) { - u64 idle_start = getCurUs(); - - if (!netif_running(odev)) { + /* Did we saturate the queue already? */ + if (netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)) { + /* If device is down, then all queues are permnantly frozen */ + if (netif_running(odev)) + idle(pkt_dev); + else pktgen_stop_device(pkt_dev); - goto out; - } - if (need_resched()) - schedule(); - - pkt_dev->idle_acc += getCurUs() - idle_start; - - if (netif_tx_queue_stopped(txq) || - netif_tx_queue_frozen(txq)) { - pkt_dev->next_tx_us = getCurUs(); /* TODO */ - pkt_dev->next_tx_ns = 0; - goto out; /* Try the next interface */ - } + return; } if (pkt_dev->last_ok || !pkt_dev->skb) { @@ -3407,7 +3407,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) "allocate skb in fill_packet.\n"); schedule(); pkt_dev->clone_count--; /* back out increment, OOM */ - goto out; + return; } pkt_dev->allocated_skbs++; pkt_dev->clone_count = 0; /* reset counter */ @@ -3419,9 +3419,9 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) txq = netdev_get_tx_queue(odev, queue_map); __netif_tx_lock_bh(txq); - if (!netif_tx_queue_stopped(txq) && - !netif_tx_queue_frozen(txq)) { - + if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq))) + pkt_dev->last_ok = 0; + else { atomic_inc(&(pkt_dev->skb->users)); retry_now: ret = (*xmit)(pkt_dev->skb, odev); @@ -3458,13 +3458,6 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->next_tx_ns -= 1000; } } - - else { /* Retry it next time */ - pkt_dev->last_ok = 0; - pkt_dev->next_tx_us = getCurUs(); /* TODO */ - pkt_dev->next_tx_ns = 0; - } - __netif_tx_unlock_bh(txq); /* If pkt_dev->count is zero, then run forever */ -- cgit v1.1 From 7d7bb1cf0ea7434230b3e3de49c24ff68666a72e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:12 +0000 Subject: pktgen: cleanup clone count test The if statement to test for "should a new packet be used" can be simplified. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 89fd232..90d48cc 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3395,23 +3395,22 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) return; } - if (pkt_dev->last_ok || !pkt_dev->skb) { - if ((++pkt_dev->clone_count >= pkt_dev->clone_skb) - || (!pkt_dev->skb)) { - /* build a new pkt */ - kfree_skb(pkt_dev->skb); - - pkt_dev->skb = fill_packet(odev, pkt_dev); - if (pkt_dev->skb == NULL) { - printk(KERN_ERR "pktgen: ERROR: couldn't " - "allocate skb in fill_packet.\n"); - schedule(); - pkt_dev->clone_count--; /* back out increment, OOM */ - return; - } - pkt_dev->allocated_skbs++; - pkt_dev->clone_count = 0; /* reset counter */ + if (!pkt_dev->skb || (pkt_dev->last_ok && + ++pkt_dev->clone_count >= pkt_dev->clone_skb)) { + /* build a new pkt */ + kfree_skb(pkt_dev->skb); + + pkt_dev->skb = fill_packet(odev, pkt_dev); + if (pkt_dev->skb == NULL) { + printk(KERN_ERR "pktgen: ERROR: couldn't " + "allocate skb in fill_packet.\n"); + schedule(); + pkt_dev->clone_count--; /* back out increment, OOM */ + return; } + + pkt_dev->allocated_skbs++; + pkt_dev->clone_count = 0; /* reset counter */ } /* fill_packet() might have changed the queue */ @@ -3476,7 +3475,6 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) /* Done with this */ pktgen_stop_device(pkt_dev); } -out:; } /* -- cgit v1.1 From e470757d6179eb8998881ac2706927d4d0cb0e54 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:13 +0000 Subject: pktgen: use netdev_alloc_skb netdev_alloc_skb is NUMA node aware. Also, don't exhaust atomic emergency pool. Don't want pktgen to cause OOM behaviour. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 90d48cc..2456bd5 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2539,8 +2539,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, mod_cur_headers(pkt_dev); datalen = (odev->hard_header_len + 16) & ~0xf; - skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen + - pkt_dev->pkt_overhead, GFP_ATOMIC); + skb = __netdev_alloc_skb(odev, + pkt_dev->cur_pkt_size + 64 + + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT); if (!skb) { sprintf(pkt_dev->result, "No memory"); return NULL; @@ -2878,8 +2879,9 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, queue_map = pkt_dev->cur_queue_map; mod_cur_headers(pkt_dev); - skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 + - pkt_dev->pkt_overhead, GFP_ATOMIC); + skb = __netdev_alloc_skb(odev, + pkt_dev->cur_pkt_size + 64 + + 16 + pkt_dev->pkt_overhead, GFP_NOWAIT); if (!skb) { sprintf(pkt_dev->result, "No memory"); return NULL; -- cgit v1.1 From 5b8db2f568c4ee2a436f0e1c1416153e2878f869 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:14 +0000 Subject: pktgen: reorganize transmit loop Handle standard (and non-standard) return values in a switch. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 2456bd5..bce9bf4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3424,27 +3424,29 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->last_ok = 0; else { atomic_inc(&(pkt_dev->skb->users)); - retry_now: + + retry_now: ret = (*xmit)(pkt_dev->skb, odev); - if (likely(ret == NETDEV_TX_OK)) { + switch (ret) { + case NETDEV_TX_OK: txq_trans_update(txq); pkt_dev->last_ok = 1; pkt_dev->sofar++; pkt_dev->seq_num++; pkt_dev->tx_bytes += pkt_dev->cur_pkt_size; - - } else if (ret == NETDEV_TX_LOCKED - && (odev->features & NETIF_F_LLTX)) { + break; + case NETDEV_TX_LOCKED: cpu_relax(); goto retry_now; - } else { /* Retry it next time */ - - atomic_dec(&(pkt_dev->skb->users)); - - if (debug && net_ratelimit()) - printk(KERN_INFO "pktgen: Hard xmit error\n"); - + default: /* Drivers are not supposed to return other values! */ + if (net_ratelimit()) + pr_info("pktgen: %s xmit error: %d\n", + odev->name, ret); pkt_dev->errors++; + /* fallthru */ + case NETDEV_TX_BUSY: + /* Retry it next time */ + atomic_dec(&(pkt_dev->skb->users)); pkt_dev->last_ok = 0; } -- cgit v1.1 From 5c9d191c166233e723e632e79bcca2127a5fece9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:15 +0000 Subject: pktgen: avoid calling gettimeofday If not using delay then no need to update next_tx after each packet sent. This allows pktgen to send faster especially on systems with slower clock sources. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index bce9bf4..b7302e1 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3450,15 +3450,17 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->last_ok = 0; } - pkt_dev->next_tx_us = getCurUs(); - pkt_dev->next_tx_ns = 0; + if (pkt_dev->delay_us || pkt_dev->delay_ns) { + pkt_dev->next_tx_us = getCurUs(); + pkt_dev->next_tx_ns = 0; - pkt_dev->next_tx_us += pkt_dev->delay_us; - pkt_dev->next_tx_ns += pkt_dev->delay_ns; + pkt_dev->next_tx_us += pkt_dev->delay_us; + pkt_dev->next_tx_ns += pkt_dev->delay_ns; - if (pkt_dev->next_tx_ns > 1000) { - pkt_dev->next_tx_us++; - pkt_dev->next_tx_ns -= 1000; + if (pkt_dev->next_tx_ns > 1000) { + pkt_dev->next_tx_us++; + pkt_dev->next_tx_ns -= 1000; + } } } __netif_tx_unlock_bh(txq); -- cgit v1.1 From fd29cf72621071d1d5f9bae634a4505b05f0e58b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:16 +0000 Subject: pktgen: convert to use ktime_t The kernel ktime_t is a nice generic infrastructure for mananging high resolution times, as is done in pktgen. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 184 +++++++++++++++++++++++++----------------------------- 1 file changed, 84 insertions(+), 100 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b7302e1..bede00b 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -246,16 +246,14 @@ struct pktgen_dev { int max_pkt_size; /* = ETH_ZLEN; */ int pkt_overhead; /* overhead for MPLS, VLANs, IPSEC etc */ int nfrags; - __u32 delay_us; /* Default delay */ - __u32 delay_ns; + u64 delay; /* nano-seconds */ + __u64 count; /* Default No packets to send */ __u64 sofar; /* How many pkts we've sent so far */ __u64 tx_bytes; /* How many bytes we've transmitted */ __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */ /* runtime counters relating to clone_skb */ - __u64 next_tx_us; /* timestamp of when to tx next */ - __u32 next_tx_ns; __u64 allocated_skbs; __u32 clone_count; @@ -263,9 +261,11 @@ struct pktgen_dev { * Or a failed transmit of some sort? This will keep * sequence numbers in order, for example. */ - __u64 started_at; /* micro-seconds */ - __u64 stopped_at; /* micro-seconds */ - __u64 idle_acc; /* micro-seconds */ + ktime_t next_tx; + ktime_t started_at; + ktime_t stopped_at; + u64 idle_acc; /* nano-seconds */ + __u32 seq_num; int clone_skb; /* Use multiple SKBs during packet gen. If this number @@ -397,23 +397,20 @@ struct pktgen_thread { #define REMOVE 1 #define FIND 0 -/** Convert to micro-seconds */ -static inline __u64 tv_to_us(const struct timeval *tv) +static inline ktime_t ktime_now(void) { - __u64 us = tv->tv_usec; - us += (__u64) tv->tv_sec * (__u64) 1000000; - return us; + struct timespec ts; + ktime_get_ts(&ts); + + return timespec_to_ktime(ts); } -static __u64 getCurUs(void) +/* This works even if 32 bit because of careful byte order choice */ +static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2) { - struct timeval tv; - do_gettimeofday(&tv); - return tv_to_us(&tv); + return cmp1.tv64 < cmp2.tv64; } -/* old include end */ - static const char version[] __initconst = VERSION; static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i); @@ -510,9 +507,8 @@ static const struct file_operations pktgen_fops = { static int pktgen_if_show(struct seq_file *seq, void *v) { const struct pktgen_dev *pkt_dev = seq->private; - __u64 sa; - __u64 stopped; - __u64 now = getCurUs(); + ktime_t stopped; + u64 idle; seq_printf(seq, "Params: count %llu min_pkt_size: %u max_pkt_size: %u\n", @@ -520,9 +516,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v) pkt_dev->max_pkt_size); seq_printf(seq, - " frags: %d delay: %u clone_skb: %d ifname: %s\n", - pkt_dev->nfrags, - 1000 * pkt_dev->delay_us + pkt_dev->delay_ns, + " frags: %d delay: %llu clone_skb: %d ifname: %s\n", + pkt_dev->nfrags, (unsigned long long) pkt_dev->delay, pkt_dev->clone_skb, pkt_dev->odev->name); seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows, @@ -654,17 +649,21 @@ static int pktgen_if_show(struct seq_file *seq, void *v) seq_puts(seq, "\n"); - sa = pkt_dev->started_at; - stopped = pkt_dev->stopped_at; - if (pkt_dev->running) - stopped = now; /* not really stopped, more like last-running-at */ + /* not really stopped, more like last-running-at */ + stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at; + idle = pkt_dev->idle_acc; + do_div(idle, NSEC_PER_USEC); seq_printf(seq, - "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n", + "Current:\n pkts-sofar: %llu errors: %llu\n", (unsigned long long)pkt_dev->sofar, - (unsigned long long)pkt_dev->errors, (unsigned long long)sa, - (unsigned long long)stopped, - (unsigned long long)pkt_dev->idle_acc); + (unsigned long long)pkt_dev->errors); + + seq_printf(seq, + " started: %lluus stopped: %lluus idle: %lluus\n", + (unsigned long long) ktime_to_us(pkt_dev->started_at), + (unsigned long long) ktime_to_us(stopped), + (unsigned long long) idle); seq_printf(seq, " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n", @@ -950,15 +949,13 @@ static ssize_t pktgen_if_write(struct file *file, return len; } i += len; - if (value == 0x7FFFFFFF) { - pkt_dev->delay_us = 0x7FFFFFFF; - pkt_dev->delay_ns = 0; - } else { - pkt_dev->delay_us = value / 1000; - pkt_dev->delay_ns = value % 1000; - } - sprintf(pg_result, "OK: delay=%u", - 1000 * pkt_dev->delay_us + pkt_dev->delay_ns); + if (value == 0x7FFFFFFF) + pkt_dev->delay = ULLONG_MAX; + else + pkt_dev->delay = (u64)value * NSEC_PER_USEC; + + sprintf(pg_result, "OK: delay=%llu", + (unsigned long long) pkt_dev->delay); return count; } if (!strcmp(name, "udp_src_min")) { @@ -2089,27 +2086,33 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) pkt_dev->nflows = 0; } -static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us) +static inline s64 delta_ns(ktime_t a, ktime_t b) { - __u64 start; - __u64 now; + return ktime_to_ns(ktime_sub(a, b)); +} + +static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) +{ + ktime_t start, now; + s64 dt; + + start = now = ktime_now(); - start = now = getCurUs(); - while (now < spin_until_us) { + while ((dt = delta_ns(spin_until, now)) > 0) { /* TODO: optimize sleeping behavior */ - if (spin_until_us - now > jiffies_to_usecs(1) + 1) + if (dt > TICK_NSEC) schedule_timeout_interruptible(1); - else if (spin_until_us - now > 100) { + else if (dt > 100*NSEC_PER_USEC) { if (!pkt_dev->running) return; if (need_resched()) schedule(); } - now = getCurUs(); + now = ktime_now(); } - pkt_dev->idle_acc += now - start; + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(now, start)); } static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev) @@ -3070,9 +3073,9 @@ static void pktgen_run(struct pktgen_thread *t) pktgen_clear_counters(pkt_dev); pkt_dev->running = 1; /* Cranke yeself! */ pkt_dev->skb = NULL; - pkt_dev->started_at = getCurUs(); - pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */ - pkt_dev->next_tx_ns = 0; + pkt_dev->started_at = + pkt_dev->next_tx = ktime_now(); + set_pkt_overhead(pkt_dev); strcpy(pkt_dev->result, "Starting"); @@ -3188,28 +3191,21 @@ static void pktgen_reset_all_threads(void) static void show_results(struct pktgen_dev *pkt_dev, int nr_frags) { - __u64 total_us, bps, mbps, pps, idle; + __u64 bps, mbps, pps; char *p = pkt_dev->result; - - total_us = pkt_dev->stopped_at - pkt_dev->started_at; - - idle = pkt_dev->idle_acc; - - p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n", - (unsigned long long)total_us, - (unsigned long long)(total_us - idle), - (unsigned long long)idle, + ktime_t elapsed = ktime_sub(pkt_dev->stopped_at, + pkt_dev->started_at); + ktime_t idle = ns_to_ktime(pkt_dev->idle_acc); + + p += sprintf(p, "OK: %llu(c%llu+d%llu) nsec, %llu (%dbyte,%dfrags)\n", + (unsigned long long)ktime_to_us(elapsed), + (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)), + (unsigned long long)ktime_to_us(idle), (unsigned long long)pkt_dev->sofar, pkt_dev->cur_pkt_size, nr_frags); - pps = pkt_dev->sofar * USEC_PER_SEC; - - while ((total_us >> 32) != 0) { - pps >>= 1; - total_us >>= 1; - } - - do_div(pps, total_us); + pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC, + ktime_to_ns(elapsed)); bps = pps * 8 * pkt_dev->cur_pkt_size; @@ -3235,7 +3231,7 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev) kfree_skb(pkt_dev->skb); pkt_dev->skb = NULL; - pkt_dev->stopped_at = getCurUs(); + pkt_dev->stopped_at = ktime_now(); pkt_dev->running = 0; show_results(pkt_dev, nr_frags); @@ -3254,7 +3250,7 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t) continue; if (best == NULL) best = pkt_dev; - else if (pkt_dev->next_tx_us < best->next_tx_us) + else if (ktime_lt(pkt_dev->next_tx, best->next_tx)) best = pkt_dev; } if_unlock(t); @@ -3343,16 +3339,17 @@ static void pktgen_rem_thread(struct pktgen_thread *t) static void idle(struct pktgen_dev *pkt_dev) { - u64 idle_start = getCurUs(); + ktime_t idle_start = ktime_now(); if (need_resched()) schedule(); else cpu_relax(); - pkt_dev->idle_acc += getCurUs() - idle_start; + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); } + static void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = pkt_dev->odev; @@ -3362,19 +3359,15 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) u16 queue_map; int ret; - if (pkt_dev->delay_us || pkt_dev->delay_ns) { - u64 now; - - now = getCurUs(); - if (now < pkt_dev->next_tx_us) - spin(pkt_dev, pkt_dev->next_tx_us); + if (pkt_dev->delay) { + if (ktime_lt(ktime_now(), pkt_dev->next_tx)) + spin(pkt_dev, pkt_dev->next_tx); /* This is max DELAY, this has special meaning of * "never transmit" */ - if (pkt_dev->delay_us == 0x7FFFFFFF) { - pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us; - pkt_dev->next_tx_ns = pkt_dev->delay_ns; + if (pkt_dev->delay == ULLONG_MAX) { + pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX); return; } } @@ -3450,32 +3443,24 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->last_ok = 0; } - if (pkt_dev->delay_us || pkt_dev->delay_ns) { - pkt_dev->next_tx_us = getCurUs(); - pkt_dev->next_tx_ns = 0; - - pkt_dev->next_tx_us += pkt_dev->delay_us; - pkt_dev->next_tx_ns += pkt_dev->delay_ns; - - if (pkt_dev->next_tx_ns > 1000) { - pkt_dev->next_tx_us++; - pkt_dev->next_tx_ns -= 1000; - } - } + if (pkt_dev->delay) + pkt_dev->next_tx = ktime_add_ns(ktime_now(), + pkt_dev->delay); } __netif_tx_unlock_bh(txq); /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { if (atomic_read(&(pkt_dev->skb->users)) != 1) { - u64 idle_start = getCurUs(); + ktime_t idle_start = ktime_now(); while (atomic_read(&(pkt_dev->skb->users)) != 1) { if (signal_pending(current)) { break; } schedule(); } - pkt_dev->idle_acc += getCurUs() - idle_start; + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), + idle_start)); } /* Done with this */ @@ -3634,8 +3619,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) pkt_dev->max_pkt_size = ETH_ZLEN; pkt_dev->nfrags = 0; pkt_dev->clone_skb = pg_clone_skb_d; - pkt_dev->delay_us = pg_delay_d / 1000; - pkt_dev->delay_ns = pg_delay_d % 1000; + pkt_dev->delay = pg_delay_d; pkt_dev->count = pg_count_d; pkt_dev->sofar = 0; pkt_dev->udp_src_min = 9; /* sink port */ -- cgit v1.1 From 2bc481cf433879f0e6cdd4d899fc21ee05dcea23 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 28 Aug 2009 23:41:29 -0700 Subject: pktgen: spin using hrtimer This changes how the pktgen thread spins/waits between packets if delay is configured. It uses a high res timer to wait for time to arrive. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index bede00b..3045dd1 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -131,6 +131,7 @@ #include #include #include +#include #include #include #include @@ -2086,33 +2087,40 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) pkt_dev->nflows = 0; } -static inline s64 delta_ns(ktime_t a, ktime_t b) -{ - return ktime_to_ns(ktime_sub(a, b)); -} static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until) { - ktime_t start, now; - s64 dt; + ktime_t start; + s32 remaining; + struct hrtimer_sleeper t; - start = now = ktime_now(); + hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + hrtimer_set_expires(&t.timer, spin_until); + + remaining = ktime_to_us(hrtimer_expires_remaining(&t.timer)); + if (remaining <= 0) + return; - while ((dt = delta_ns(spin_until, now)) > 0) { - /* TODO: optimize sleeping behavior */ - if (dt > TICK_NSEC) - schedule_timeout_interruptible(1); - else if (dt > 100*NSEC_PER_USEC) { - if (!pkt_dev->running) - return; - if (need_resched()) + start = ktime_now(); + if (remaining < 100) + udelay(remaining); /* really small just spin */ + else { + /* see do_nanosleep */ + hrtimer_init_sleeper(&t, current); + do { + set_current_state(TASK_INTERRUPTIBLE); + hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS); + if (!hrtimer_active(&t.timer)) + t.task = NULL; + + if (likely(t.task)) schedule(); - } - now = ktime_now(); + hrtimer_cancel(&t.timer); + } while (t.task && pkt_dev->running && !signal_pending(current)); + __set_current_state(TASK_RUNNING); } - - pkt_dev->idle_acc += ktime_to_ns(ktime_sub(now, start)); + pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), start)); } static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev) @@ -3360,8 +3368,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) int ret; if (pkt_dev->delay) { - if (ktime_lt(ktime_now(), pkt_dev->next_tx)) - spin(pkt_dev, pkt_dev->next_tx); + spin(pkt_dev, pkt_dev->next_tx); /* This is max DELAY, this has special meaning of * "never transmit" -- cgit v1.1 From 64e8ff5ef2a798cae2e3bede75644173aae98e08 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:18 +0000 Subject: pktgen: use common idle routine Simpler to have one place that spins and accounts for delays, this will also make the last packet be detected faster for more repeatable timing. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 3045dd1..ad41f06 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3458,16 +3458,10 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { - if (atomic_read(&(pkt_dev->skb->users)) != 1) { - ktime_t idle_start = ktime_now(); - while (atomic_read(&(pkt_dev->skb->users)) != 1) { - if (signal_pending(current)) { - break; - } - schedule(); - } - pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), - idle_start)); + while (atomic_read(&(pkt_dev->skb->users)) != 1) { + if (signal_pending(current)) + break; + idle(pkt_dev); } /* Done with this */ -- cgit v1.1 From 63adc6fb8ac0dee0020d6ad73e0d44f4306e1e34 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:19 +0000 Subject: pktgen: cleanup checkpatch warnings Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 298 +++++++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 139 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index ad41f06..51b1e61 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -163,13 +163,14 @@ #include #include #include -#include +#include +#include +#include #include -#include #include /* do_div */ -#include -#define VERSION "pktgen v2.70: Packet Generator for packet performance testing.\n" +#define VERSION \ + "pktgen v2.70: Packet Generator for packet performance testing.\n" #define IP_NAME_SZ 32 #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */ @@ -207,7 +208,7 @@ #define PKTGEN_MAGIC 0xbe9be955 #define PG_PROC_DIR "pktgen" #define PGCTRL "pgctrl" -static struct proc_dir_entry *pg_proc_dir = NULL; +static struct proc_dir_entry *pg_proc_dir; #define MAX_CFLOWS 65536 @@ -232,9 +233,9 @@ struct pktgen_dev { */ struct proc_dir_entry *entry; /* proc file */ struct pktgen_thread *pg_thread;/* the owner */ - struct list_head list; /* Used for chaining in the thread's run-queue */ + struct list_head list; /* chaining in the thread's run-queue */ - int running; /* if this changes to false, the test will stop */ + int running; /* if false, the test will stop */ /* If min != max, then we will either do a linear iteration, or * we will do a random selection from within the range. @@ -252,15 +253,16 @@ struct pktgen_dev { __u64 count; /* Default No packets to send */ __u64 sofar; /* How many pkts we've sent so far */ __u64 tx_bytes; /* How many bytes we've transmitted */ - __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */ + __u64 errors; /* Errors when trying to transmit, + pkts will be re-sent */ /* runtime counters relating to clone_skb */ __u64 allocated_skbs; __u32 clone_count; int last_ok; /* Was last skb sent? - * Or a failed transmit of some sort? This will keep - * sequence numbers in order, for example. + * Or a failed transmit of some sort? + * This will keep sequence numbers in order */ ktime_t next_tx; ktime_t started_at; @@ -269,11 +271,14 @@ struct pktgen_dev { __u32 seq_num; - int clone_skb; /* Use multiple SKBs during packet gen. If this number - * is greater than 1, then that many copies of the same - * packet will be sent before a new packet is allocated. - * For instance, if you want to send 1024 identical packets - * before creating a new packet, set clone_skb to 1024. + int clone_skb; /* + * Use multiple SKBs during packet gen. + * If this number is greater than 1, then + * that many copies of the same packet will be + * sent before a new packet is allocated. + * If you want to send 1024 identical packets + * before creating a new packet, + * set clone_skb to 1024. */ char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */ @@ -305,8 +310,10 @@ struct pktgen_dev { __u16 udp_dst_max; /* exclusive, dest UDP port */ /* DSCP + ECN */ - __u8 tos; /* six most significant bits of (former) IPv4 TOS are for dscp codepoint */ - __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 (see RFC 3260, sec. 4) */ + __u8 tos; /* six MSB of (former) IPv4 TOS + are for dscp codepoint */ + __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 + (see RFC 3260, sec. 4) */ /* MPLS */ unsigned nr_labels; /* Depth of stack, 0 = no MPLS */ @@ -347,15 +354,17 @@ struct pktgen_dev { */ __u16 pad; /* pad out the hh struct to an even 16 bytes */ - struct sk_buff *skb; /* skb we are to transmit next, mainly used for when we + struct sk_buff *skb; /* skb we are to transmit next, used for when we * are transmitting the same one multiple times */ - struct net_device *odev; /* The out-going device. Note that the device should - * have it's pg_info pointer pointing back to this - * device. This will be set when the user specifies - * the out-going device name (not when the inject is - * started as it used to do.) - */ + struct net_device *odev; /* The out-going device. + * Note that the device should have it's + * pg_info pointer pointing back to this + * device. + * Set when the user specifies the out-going + * device name (not when the inject is + * started as it used to do.) + */ struct flow_state *flows; unsigned cflows; /* Concurrent flows (config) */ unsigned lflow; /* Flow length (config) */ @@ -380,13 +389,14 @@ struct pktgen_hdr { }; struct pktgen_thread { - spinlock_t if_lock; + spinlock_t if_lock; /* for list of devices */ struct list_head if_list; /* All device here */ struct list_head th_list; struct task_struct *tsk; char result[512]; - /* Field for thread to receive "posted" events terminate, stop ifs etc. */ + /* Field for thread to receive "posted" events terminate, + stop ifs etc. */ u32 control; int cpu; @@ -453,8 +463,8 @@ static int pgctrl_show(struct seq_file *seq, void *v) return 0; } -static ssize_t pgctrl_write(struct file *file, const char __user * buf, - size_t count, loff_t * ppos) +static ssize_t pgctrl_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { int err = 0; char data[128]; @@ -545,11 +555,14 @@ static int pktgen_if_show(struct seq_file *seq, void *v) " daddr: %s min_daddr: %s max_daddr: %s\n", b1, b2, b3); - } else + } else { + seq_printf(seq, + " dst_min: %s dst_max: %s\n", + pkt_dev->dst_min, pkt_dev->dst_max); seq_printf(seq, - " dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n", - pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min, - pkt_dev->src_max); + " src_min: %s src_max: %s\n", + pkt_dev->src_min, pkt_dev->src_max); + } seq_puts(seq, " src_mac: "); @@ -561,7 +574,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v) seq_printf(seq, "%pM\n", pkt_dev->dst_mac); seq_printf(seq, - " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n", + " udp_src_min: %d udp_src_max: %d" + " udp_dst_min: %d udp_dst_max: %d\n", pkt_dev->udp_src_min, pkt_dev->udp_src_max, pkt_dev->udp_dst_min, pkt_dev->udp_dst_max); @@ -577,23 +591,21 @@ static int pktgen_if_show(struct seq_file *seq, void *v) i == pkt_dev->nr_labels-1 ? "\n" : ", "); } - if (pkt_dev->vlan_id != 0xffff) { + if (pkt_dev->vlan_id != 0xffff) seq_printf(seq, " vlan_id: %u vlan_p: %u vlan_cfi: %u\n", - pkt_dev->vlan_id, pkt_dev->vlan_p, pkt_dev->vlan_cfi); - } + pkt_dev->vlan_id, pkt_dev->vlan_p, + pkt_dev->vlan_cfi); - if (pkt_dev->svlan_id != 0xffff) { + if (pkt_dev->svlan_id != 0xffff) seq_printf(seq, " svlan_id: %u vlan_p: %u vlan_cfi: %u\n", - pkt_dev->svlan_id, pkt_dev->svlan_p, pkt_dev->svlan_cfi); - } + pkt_dev->svlan_id, pkt_dev->svlan_p, + pkt_dev->svlan_cfi); - if (pkt_dev->tos) { + if (pkt_dev->tos) seq_printf(seq, " tos: 0x%02x\n", pkt_dev->tos); - } - if (pkt_dev->traffic_class) { + if (pkt_dev->traffic_class) seq_printf(seq, " traffic_class: 0x%02x\n", pkt_dev->traffic_class); - } seq_printf(seq, " Flags: "); @@ -696,7 +708,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v) } -static int hex32_arg(const char __user *user_buffer, unsigned long maxlen, __u32 *num) +static int hex32_arg(const char __user *user_buffer, unsigned long maxlen, + __u32 *num) { int i = 0; *num = 0; @@ -846,9 +859,9 @@ static ssize_t pktgen_if_write(struct file *file, /* Read variable name */ len = strn_len(&user_buffer[i], sizeof(name) - 1); - if (len < 0) { + if (len < 0) return len; - } + memset(name, 0, sizeof(name)); if (copy_from_user(name, &user_buffer[i], len)) return -EFAULT; @@ -872,9 +885,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "min_pkt_size")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value < 14 + 20 + 8) value = 14 + 20 + 8; @@ -889,9 +902,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "max_pkt_size")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value < 14 + 20 + 8) value = 14 + 20 + 8; @@ -908,9 +921,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "pkt_size")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value < 14 + 20 + 8) value = 14 + 20 + 8; @@ -925,9 +938,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "debug")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; debug = value; sprintf(pg_result, "OK: debug=%u", debug); @@ -936,9 +949,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "frags")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->nfrags = value; sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags); @@ -946,9 +959,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "delay")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value == 0x7FFFFFFF) pkt_dev->delay = ULLONG_MAX; @@ -961,9 +974,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "udp_src_min")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value != pkt_dev->udp_src_min) { pkt_dev->udp_src_min = value; @@ -974,9 +987,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "udp_dst_min")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value != pkt_dev->udp_dst_min) { pkt_dev->udp_dst_min = value; @@ -987,9 +1000,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "udp_src_max")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value != pkt_dev->udp_src_max) { pkt_dev->udp_src_max = value; @@ -1000,9 +1013,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "udp_dst_max")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value != pkt_dev->udp_dst_max) { pkt_dev->udp_dst_max = value; @@ -1013,9 +1026,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "clone_skb")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->clone_skb = value; @@ -1024,9 +1037,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "count")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->count = value; sprintf(pg_result, "OK: count=%llu", @@ -1035,9 +1048,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "src_mac_count")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (pkt_dev->src_mac_count != value) { pkt_dev->src_mac_count = value; @@ -1049,9 +1062,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "dst_mac_count")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (pkt_dev->dst_mac_count != value) { pkt_dev->dst_mac_count = value; @@ -1065,9 +1078,9 @@ static ssize_t pktgen_if_write(struct file *file, char f[32]; memset(f, 0, 32); len = strn_len(&user_buffer[i], sizeof(f) - 1); - if (len < 0) { + if (len < 0) return len; - } + if (copy_from_user(f, &user_buffer[i], len)) return -EFAULT; i += len; @@ -1166,9 +1179,8 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) { len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1); - if (len < 0) { + if (len < 0) return len; - } if (copy_from_user(buf, &user_buffer[i], len)) return -EFAULT; @@ -1188,9 +1200,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "dst_max")) { len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1); - if (len < 0) { + if (len < 0) return len; - } + if (copy_from_user(buf, &user_buffer[i], len)) return -EFAULT; @@ -1301,9 +1313,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "src_min")) { len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1); - if (len < 0) { + if (len < 0) return len; - } + if (copy_from_user(buf, &user_buffer[i], len)) return -EFAULT; buf[len] = 0; @@ -1322,9 +1334,9 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "src_max")) { len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1); - if (len < 0) { + if (len < 0) return len; - } + if (copy_from_user(buf, &user_buffer[i], len)) return -EFAULT; buf[len] = 0; @@ -1348,9 +1360,9 @@ static ssize_t pktgen_if_write(struct file *file, memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN); len = strn_len(&user_buffer[i], sizeof(valstr) - 1); - if (len < 0) { + if (len < 0) return len; - } + memset(valstr, 0, sizeof(valstr)); if (copy_from_user(valstr, &user_buffer[i], len)) return -EFAULT; @@ -1390,9 +1402,9 @@ static ssize_t pktgen_if_write(struct file *file, memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN); len = strn_len(&user_buffer[i], sizeof(valstr) - 1); - if (len < 0) { + if (len < 0) return len; - } + memset(valstr, 0, sizeof(valstr)); if (copy_from_user(valstr, &user_buffer[i], len)) return -EFAULT; @@ -1433,9 +1445,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "flows")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value > MAX_CFLOWS) value = MAX_CFLOWS; @@ -1447,9 +1459,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "flowlen")) { len = num_arg(&user_buffer[i], 10, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->lflow = value; sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow); @@ -1458,9 +1470,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "queue_map_min")) { len = num_arg(&user_buffer[i], 5, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->queue_map_min = value; sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min); @@ -1469,9 +1481,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "queue_map_max")) { len = num_arg(&user_buffer[i], 5, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; pkt_dev->queue_map_max = value; sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max); @@ -1503,9 +1515,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "vlan_id")) { len = num_arg(&user_buffer[i], 4, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (value <= 4095) { pkt_dev->vlan_id = value; /* turn on VLAN */ @@ -1530,9 +1542,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "vlan_p")) { len = num_arg(&user_buffer[i], 1, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) { pkt_dev->vlan_p = value; @@ -1545,9 +1557,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "vlan_cfi")) { len = num_arg(&user_buffer[i], 1, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) { pkt_dev->vlan_cfi = value; @@ -1560,9 +1572,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "svlan_id")) { len = num_arg(&user_buffer[i], 4, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) { pkt_dev->svlan_id = value; /* turn on SVLAN */ @@ -1587,9 +1599,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "svlan_p")) { len = num_arg(&user_buffer[i], 1, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) { pkt_dev->svlan_p = value; @@ -1602,9 +1614,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "svlan_cfi")) { len = num_arg(&user_buffer[i], 1, &value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) { pkt_dev->svlan_cfi = value; @@ -1618,9 +1630,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "tos")) { __u32 tmp_value = 0; len = hex32_arg(&user_buffer[i], 2, &tmp_value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (len == 2) { pkt_dev->tos = tmp_value; @@ -1634,9 +1646,9 @@ static ssize_t pktgen_if_write(struct file *file, if (!strcmp(name, "traffic_class")) { __u32 tmp_value = 0; len = hex32_arg(&user_buffer[i], 2, &tmp_value); - if (len < 0) { + if (len < 0) return len; - } + i += len; if (len == 2) { pkt_dev->traffic_class = tmp_value; @@ -1906,13 +1918,14 @@ static int pktgen_device_event(struct notifier_block *unused, return NOTIFY_DONE; } -static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, const char *ifname) +static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, + const char *ifname) { char b[IFNAMSIZ+5]; int i = 0; - for(i=0; ifname[i] != '@'; i++) { - if(i == IFNAMSIZ) + for (i = 0; ifname[i] != '@'; i++) { + if (i == IFNAMSIZ) break; b[i] = ifname[i]; @@ -1979,7 +1992,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) printk(KERN_WARNING "pktgen: WARNING: Requested " "queue_map_min (zero-based) (%d) exceeds valid range " "[0 - %d] for (%d) queues on %s, resetting\n", - pkt_dev->queue_map_min, (ntxq ?: 1)- 1, ntxq, + pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq, pkt_dev->odev->name); pkt_dev->queue_map_min = ntxq - 1; } @@ -1987,7 +2000,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) printk(KERN_WARNING "pktgen: WARNING: Requested " "queue_map_max (zero-based) (%d) exceeds valid range " "[0 - %d] for (%d) queues on %s, resetting\n", - pkt_dev->queue_map_max, (ntxq ?: 1)- 1, ntxq, + pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq, pkt_dev->odev->name); pkt_dev->queue_map_max = ntxq - 1; } @@ -2028,7 +2041,8 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) */ rcu_read_lock(); - if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) { + idev = __in6_dev_get(pkt_dev->odev); + if (idev) { struct inet6_ifaddr *ifp; read_lock_bh(&idev->lock); @@ -2181,7 +2195,7 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) if (x) { pkt_dev->flows[flow].x = x; set_pkt_overhead(pkt_dev); - pkt_dev->pkt_overhead+=x->props.header_len; + pkt_dev->pkt_overhead += x->props.header_len; } } @@ -2320,18 +2334,18 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) if (!(pkt_dev->flags & F_IPV6)) { - if ((imn = ntohl(pkt_dev->saddr_min)) < (imx = - ntohl(pkt_dev-> - saddr_max))) { + imn = ntohl(pkt_dev->saddr_min); + imx = ntohl(pkt_dev->saddr_max); + if (imn < imx) { __u32 t; if (pkt_dev->flags & F_IPSRC_RND) t = random32() % (imx - imn) + imn; else { t = ntohl(pkt_dev->cur_saddr); t++; - if (t > imx) { + if (t > imx) t = imn; - } + } pkt_dev->cur_saddr = htonl(t); } @@ -2442,7 +2456,7 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev) if (err) goto error; - x->curlft.bytes +=skb->len; + x->curlft.bytes += skb->len; x->curlft.packets++; error: spin_unlock(&x->lock); @@ -2474,11 +2488,11 @@ static int process_ipsec(struct pktgen_dev *pkt_dev, int ret; __u8 *eth; nhead = x->props.header_len - skb_headroom(skb); - if (nhead >0) { + if (nhead > 0) { ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); if (ret < 0) { printk(KERN_ERR "Error expanding " - "ipsec packet %d\n",ret); + "ipsec packet %d\n", ret); goto err; } } @@ -2488,13 +2502,13 @@ static int process_ipsec(struct pktgen_dev *pkt_dev, ret = pktgen_output_ipsec(skb, pkt_dev); if (ret) { printk(KERN_ERR "Error creating ipsec " - "packet %d\n",ret); + "packet %d\n", ret); goto err; } /* restore ll */ eth = (__u8 *) skb_push(skb, ETH_HLEN); memcpy(eth, pkt_dev->hh, 12); - *(u16 *) & eth[12] = protocol; + *(u16 *) ð[12] = protocol; } } return 1; @@ -2507,9 +2521,9 @@ err: static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev) { unsigned i; - for (i = 0; i < pkt_dev->nr_labels; i++) { + for (i = 0; i < pkt_dev->nr_labels; i++) *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM; - } + mpls--; *mpls |= MPLS_STACK_BOTTOM; } @@ -2676,8 +2690,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, } } - /* Stamp the time, and sequence number, convert them to network byte order */ - + /* Stamp the time, and sequence number, + * convert them to network byte order + */ if (pgh) { struct timeval timestamp; @@ -2931,7 +2946,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, udph = udp_hdr(skb); memcpy(eth, pkt_dev->hh, 12); - *(__be16 *) & eth[12] = protocol; + *(__be16 *) ð[12] = protocol; /* Eth + IPh + UDPh + mpls */ datalen = pkt_dev->cur_pkt_size - 14 - @@ -3025,8 +3040,10 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, } } - /* Stamp the time, and sequence number, convert them to network byte order */ - /* should we update cloned packets too ? */ + /* Stamp the time, and sequence number, + * convert them to network byte order + * should we update cloned packets too ? + */ if (pgh) { struct timeval timestamp; @@ -3174,7 +3191,8 @@ static void pktgen_run_all_threads(void) mutex_unlock(&pktgen_thread_lock); - schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */ + /* Propagate thread->control */ + schedule_timeout_interruptible(msecs_to_jiffies(125)); pktgen_wait_all_threads_run(); } @@ -3192,7 +3210,8 @@ static void pktgen_reset_all_threads(void) mutex_unlock(&pktgen_thread_lock); - schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */ + /* Propagate thread->control */ + schedule_timeout_interruptible(msecs_to_jiffies(125)); pktgen_wait_all_threads_run(); } @@ -3485,7 +3504,8 @@ static int pktgen_thread_worker(void *arg) init_waitqueue_head(&t->queue); complete(&t->start_done); - pr_debug("pktgen: starting pktgen/%d: pid=%d\n", cpu, task_pid_nr(current)); + pr_debug("pktgen: starting pktgen/%d: pid=%d\n", + cpu, task_pid_nr(current)); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.1 From c3d2f52dd40b5b6c122329dac32dc0e0351f0598 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 27 Aug 2009 13:55:20 +0000 Subject: pktgen: increase version Increase module version, and cleanup module info. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/pktgen.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 51b1e61..42e4cce 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -169,9 +169,7 @@ #include #include /* do_div */ -#define VERSION \ - "pktgen v2.70: Packet Generator for packet performance testing.\n" - +#define VERSION "2.72" #define IP_NAME_SZ 32 #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */ #define MPLS_STACK_BOTTOM htonl(0x00000100) @@ -422,7 +420,8 @@ static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2) return cmp1.tv64 < cmp2.tv64; } -static const char version[] __initconst = VERSION; +static const char version[] = + "pktgen " VERSION ": Packet Generator for packet performance testing.\n"; static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i); static int pktgen_add_device(struct pktgen_thread *t, const char *ifname); @@ -459,7 +458,7 @@ static struct notifier_block pktgen_notifier_block = { static int pgctrl_show(struct seq_file *seq, void *v) { - seq_puts(seq, VERSION); + seq_puts(seq, version); return 0; } @@ -3852,10 +3851,15 @@ static void __exit pg_cleanup(void) module_init(pg_init); module_exit(pg_cleanup); -MODULE_AUTHOR("Robert Olsson "); MODULE_DESCRIPTION("Packet Generator tool"); MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION); module_param(pg_count_d, int, 0); +MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject"); module_param(pg_delay_d, int, 0); +MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)"); module_param(pg_clone_skb_d, int, 0); +MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet"); module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Enable debugging of pktgen module"); -- cgit v1.1 From 2975315b79f4109523b3d43932f57ed8370b9da3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 28 Aug 2009 23:34:43 -0700 Subject: pktgen: use proc_create_data() It looks like after rename device proc entry is unusable, because of no ->read_proc or ->proc_fops. And create_proc_entry() is deprecated. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/pktgen.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 42e4cce..2001cb1 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1882,8 +1882,10 @@ static void pktgen_change_name(struct net_device *dev) remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); - pkt_dev->entry = create_proc_entry(dev->name, 0600, - pg_proc_dir); + pkt_dev->entry = proc_create_data(dev->name, 0600, + pg_proc_dir, + &pktgen_if_fops, + pkt_dev); if (!pkt_dev->entry) printk(KERN_ERR "pktgen: can't move proc " " entry for '%s'\n", dev->name); -- cgit v1.1 From 3d1427f87002735aa54c370558e0c2bacc61f31e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Aug 2009 23:45:21 -0700 Subject: ipv4: af_inet.c cleanups Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 112 ++++++++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 57 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 197d024..6c30a73 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -124,7 +124,6 @@ static struct list_head inetsw[SOCK_MAX]; static DEFINE_SPINLOCK(inetsw_lock); struct ipv4_config ipv4_config; - EXPORT_SYMBOL(ipv4_config); /* New destruction routine */ @@ -139,12 +138,12 @@ void inet_sock_destruct(struct sock *sk) sk_mem_reclaim(sk); if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) { - printk("Attempt to release TCP socket in state %d %p\n", + pr_err("Attempt to release TCP socket in state %d %p\n", sk->sk_state, sk); return; } if (!sock_flag(sk, SOCK_DEAD)) { - printk("Attempt to release alive inet socket %p\n", sk); + pr_err("Attempt to release alive inet socket %p\n", sk); return; } @@ -157,6 +156,7 @@ void inet_sock_destruct(struct sock *sk) dst_release(sk->sk_dst_cache); sk_refcnt_debug_dec(sk); } +EXPORT_SYMBOL(inet_sock_destruct); /* * The routines beyond this point handle the behaviour of an AF_INET @@ -219,6 +219,7 @@ out: release_sock(sk); return err; } +EXPORT_SYMBOL(inet_listen); u32 inet_ehash_secret __read_mostly; EXPORT_SYMBOL(inet_ehash_secret); @@ -435,9 +436,11 @@ int inet_release(struct socket *sock) } return 0; } +EXPORT_SYMBOL(inet_release); /* It is off by default, see below. */ int sysctl_ip_nonlocal_bind __read_mostly; +EXPORT_SYMBOL(sysctl_ip_nonlocal_bind); int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { @@ -519,6 +522,7 @@ out_release_sock: out: return err; } +EXPORT_SYMBOL(inet_bind); int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr, int addr_len, int flags) @@ -532,6 +536,7 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr, return -EAGAIN; return sk->sk_prot->connect(sk, (struct sockaddr *)uaddr, addr_len); } +EXPORT_SYMBOL(inet_dgram_connect); static long inet_wait_for_connect(struct sock *sk, long timeo) { @@ -641,6 +646,7 @@ sock_error: sock->state = SS_DISCONNECTING; goto out; } +EXPORT_SYMBOL(inet_stream_connect); /* * Accept a pending connection. The TCP layer now gives BSD semantics. @@ -668,6 +674,7 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags) do_err: return err; } +EXPORT_SYMBOL(inet_accept); /* @@ -699,6 +706,7 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr, *uaddr_len = sizeof(*sin); return 0; } +EXPORT_SYMBOL(inet_getname); int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size) @@ -711,9 +719,11 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, return sk->sk_prot->sendmsg(iocb, sk, msg, size); } +EXPORT_SYMBOL(inet_sendmsg); -static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags) +static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags) { struct sock *sk = sock->sk; @@ -780,6 +790,7 @@ int inet_shutdown(struct socket *sock, int how) release_sock(sk); return err; } +EXPORT_SYMBOL(inet_shutdown); /* * ioctl() calls you can issue on an INET socket. Most of these are @@ -798,44 +809,45 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) struct net *net = sock_net(sk); switch (cmd) { - case SIOCGSTAMP: - err = sock_get_timestamp(sk, (struct timeval __user *)arg); - break; - case SIOCGSTAMPNS: - err = sock_get_timestampns(sk, (struct timespec __user *)arg); - break; - case SIOCADDRT: - case SIOCDELRT: - case SIOCRTMSG: - err = ip_rt_ioctl(net, cmd, (void __user *)arg); - break; - case SIOCDARP: - case SIOCGARP: - case SIOCSARP: - err = arp_ioctl(net, cmd, (void __user *)arg); - break; - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCSIFPFLAGS: - case SIOCGIFPFLAGS: - case SIOCSIFFLAGS: - err = devinet_ioctl(net, cmd, (void __user *)arg); - break; - default: - if (sk->sk_prot->ioctl) - err = sk->sk_prot->ioctl(sk, cmd, arg); - else - err = -ENOIOCTLCMD; - break; + case SIOCGSTAMP: + err = sock_get_timestamp(sk, (struct timeval __user *)arg); + break; + case SIOCGSTAMPNS: + err = sock_get_timestampns(sk, (struct timespec __user *)arg); + break; + case SIOCADDRT: + case SIOCDELRT: + case SIOCRTMSG: + err = ip_rt_ioctl(net, cmd, (void __user *)arg); + break; + case SIOCDARP: + case SIOCGARP: + case SIOCSARP: + err = arp_ioctl(net, cmd, (void __user *)arg); + break; + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCSIFPFLAGS: + case SIOCGIFPFLAGS: + case SIOCSIFFLAGS: + err = devinet_ioctl(net, cmd, (void __user *)arg); + break; + default: + if (sk->sk_prot->ioctl) + err = sk->sk_prot->ioctl(sk, cmd, arg); + else + err = -ENOIOCTLCMD; + break; } return err; } +EXPORT_SYMBOL(inet_ioctl); const struct proto_ops inet_stream_ops = { .family = PF_INET, @@ -862,6 +874,7 @@ const struct proto_ops inet_stream_ops = { .compat_getsockopt = compat_sock_common_getsockopt, #endif }; +EXPORT_SYMBOL(inet_stream_ops); const struct proto_ops inet_dgram_ops = { .family = PF_INET, @@ -887,6 +900,7 @@ const struct proto_ops inet_dgram_ops = { .compat_getsockopt = compat_sock_common_getsockopt, #endif }; +EXPORT_SYMBOL(inet_dgram_ops); /* * For SOCK_RAW sockets; should be the same as inet_dgram_ops but without @@ -1016,6 +1030,7 @@ out_illegal: p->type); goto out; } +EXPORT_SYMBOL(inet_register_protosw); void inet_unregister_protosw(struct inet_protosw *p) { @@ -1031,6 +1046,7 @@ void inet_unregister_protosw(struct inet_protosw *p) synchronize_net(); } } +EXPORT_SYMBOL(inet_unregister_protosw); /* * Shall we try to damage output packets if routing dev changes? @@ -1141,7 +1157,6 @@ int inet_sk_rebuild_header(struct sock *sk) return err; } - EXPORT_SYMBOL(inet_sk_rebuild_header); static int inet_gso_send_check(struct sk_buff *skb) @@ -1369,7 +1384,6 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family, } return rc; } - EXPORT_SYMBOL_GPL(inet_ctl_sock_create); unsigned long snmp_fold_field(void *mib[], int offt) @@ -1676,19 +1690,3 @@ static int __init ipv4_proc_init(void) MODULE_ALIAS_NETPROTO(PF_INET); -EXPORT_SYMBOL(inet_accept); -EXPORT_SYMBOL(inet_bind); -EXPORT_SYMBOL(inet_dgram_connect); -EXPORT_SYMBOL(inet_dgram_ops); -EXPORT_SYMBOL(inet_getname); -EXPORT_SYMBOL(inet_ioctl); -EXPORT_SYMBOL(inet_listen); -EXPORT_SYMBOL(inet_register_protosw); -EXPORT_SYMBOL(inet_release); -EXPORT_SYMBOL(inet_sendmsg); -EXPORT_SYMBOL(inet_shutdown); -EXPORT_SYMBOL(inet_sock_destruct); -EXPORT_SYMBOL(inet_stream_connect); -EXPORT_SYMBOL(inet_stream_ops); -EXPORT_SYMBOL(inet_unregister_protosw); -EXPORT_SYMBOL(sysctl_ip_nonlocal_bind); -- cgit v1.1 From df19a6267705456f463871ae2aabc44299909d2a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Aug 2009 23:48:54 -0700 Subject: tcp: keepalive cleanups Introduce keepalive_probes(tp) helper, and use it, like keepalive_time_when(tp) and keepalive_intvl_when(tp) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 6 +++--- net/ipv4/tcp_timer.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9114524..59f69a6 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2336,13 +2336,13 @@ static int do_tcp_getsockopt(struct sock *sk, int level, val = !!(tp->nonagle&TCP_NAGLE_CORK); break; case TCP_KEEPIDLE: - val = (tp->keepalive_time ? : sysctl_tcp_keepalive_time) / HZ; + val = keepalive_time_when(tp) / HZ; break; case TCP_KEEPINTVL: - val = (tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl) / HZ; + val = keepalive_intvl_when(tp) / HZ; break; case TCP_KEEPCNT: - val = tp->keepalive_probes ? : sysctl_tcp_keepalive_probes; + val = keepalive_probes(tp); break; case TCP_SYNCNT: val = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index b144a26..c520fb6 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -499,8 +499,7 @@ static void tcp_keepalive_timer (unsigned long data) elapsed = tcp_time_stamp - tp->rcv_tstamp; if (elapsed >= keepalive_time_when(tp)) { - if ((!tp->keepalive_probes && icsk->icsk_probes_out >= sysctl_tcp_keepalive_probes) || - (tp->keepalive_probes && icsk->icsk_probes_out >= tp->keepalive_probes)) { + if (icsk->icsk_probes_out >= keepalive_probes(tp)) { tcp_send_active_reset(sk, GFP_ATOMIC); tcp_write_err(sk); goto out; -- cgit v1.1 From 30038fc61adfdab162b1966e34261f06eda67f02 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Aug 2009 23:52:01 -0700 Subject: net: ip_rt_send_redirect() optimization While doing some forwarding benchmarks, I noticed ip_rt_send_redirect() is rather expensive, even if send_redirects is false for the device. Fix is to avoid two atomic ops, we dont really need to take a reference on in_dev Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/route.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index fafbe16..91867d3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1514,13 +1514,17 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) void ip_rt_send_redirect(struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); - struct in_device *in_dev = in_dev_get(rt->u.dst.dev); + struct in_device *in_dev; + int log_martians; - if (!in_dev) + rcu_read_lock(); + in_dev = __in_dev_get_rcu(rt->u.dst.dev); + if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) { + rcu_read_unlock(); return; - - if (!IN_DEV_TX_REDIRECTS(in_dev)) - goto out; + } + log_martians = IN_DEV_LOG_MARTIANS(in_dev); + rcu_read_unlock(); /* No redirected packets during ip_rt_redirect_silence; * reset the algorithm. @@ -1533,7 +1537,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) */ if (rt->u.dst.rate_tokens >= ip_rt_redirect_number) { rt->u.dst.rate_last = jiffies; - goto out; + return; } /* Check for load limit; set rate_last to the latest sent @@ -1547,7 +1551,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) rt->u.dst.rate_last = jiffies; ++rt->u.dst.rate_tokens; #ifdef CONFIG_IP_ROUTE_VERBOSE - if (IN_DEV_LOG_MARTIANS(in_dev) && + if (log_martians && rt->u.dst.rate_tokens == ip_rt_redirect_number && net_ratelimit()) printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n", @@ -1555,8 +1559,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) &rt->rt_dst, &rt->rt_gateway); #endif } -out: - in_dev_put(in_dev); } static int ip_error(struct sk_buff *skb) -- cgit v1.1 From 8945a808f7d5efd21fa9fb6055d2dd7887bdd9d8 Mon Sep 17 00:00:00 2001 From: Sascha Hlusiak Date: Fri, 28 Aug 2009 23:53:53 -0700 Subject: sit: allow ip fragmentation when using nopmtudisc to fix package loss if tunnel parameters have frag_off set to IP_DF, pmtudisc on the ipv4 link will be performed by deriving the mtu from the ipv4 link and setting the DF-Flag of the encapsulating IPv4 Header. If fragmentation is needed on the way, the IPv4 pmtu gets adjusted, the ipv6 package will be resent eventually, using the new and lower mtu and everyone is happy. If the frag_off parameter is unset, the mtu for the tunnel will be derived from the tunnel device or the ipv6 pmtu, which might be higher than the ipv4 pmtu. In that case we must allow the fragmentation of the IPv4 packet because the IPv6 mtu wouldn't 'learn' from the adjusted IPv4 pmtu, resulting in frequent icmp_frag_needed and package loss on the IPv6 layer. This patch allows fragmentation when tunnel was created with parameter nopmtudisc, like in ipip/gre tunnels. Signed-off-by: Sascha Hlusiak Signed-off-by: David S. Miller --- net/ipv6/sit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index d335a30..1d25a4d 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -778,7 +778,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) iph->version = 4; iph->ihl = sizeof(struct iphdr)>>2; if (mtu > IPV6_MIN_MTU) - iph->frag_off = htons(IP_DF); + iph->frag_off = tiph->frag_off; else iph->frag_off = 0; -- cgit v1.1 From 80b71b80df14d885f7e50e115c1348398f418759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20L=C3=A5=C3=A5s?= Date: Fri, 28 Aug 2009 23:57:15 -0700 Subject: fib_trie: resize rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here is rework and cleanup of the resize function. Some bugs we had. We were using ->parent when we should use node_parent(). Also we used ->parent which is not assigned by inflate in inflate loop. Also a fix to set thresholds to power 2 to fit halve and double strategy. max_resize is renamed to max_work which better indicates it's function. Reaching max_work is not an error, so warning is removed. max_work only limits amount of work done per resize. (limits CPU-usage, outstanding memory etc). The clean-up makes it relatively easy to add fixed sized root-nodes if we would like to decrease the memory pressure on routers with large routing tables and dynamic routing. If we'll need that... Its been tested with 280k routes. Work done together with Robert Olsson. Signed-off-by: Jens Låås Signed-off-by: Robert Olsson Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 95 +++++++++++++---------------------------------------- 1 file changed, 23 insertions(+), 72 deletions(-) (limited to 'net') diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index fe3c846..291bdf5 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -48,7 +48,7 @@ * Patrick McHardy */ -#define VERSION "0.408" +#define VERSION "0.409" #include #include @@ -325,10 +325,7 @@ static inline void check_tnode(const struct tnode *tn) static const int halve_threshold = 25; static const int inflate_threshold = 50; static const int halve_threshold_root = 15; -static const int inflate_threshold_root = 25; - -static int inflate_threshold_root_fix; -#define INFLATE_FIX_MAX 10 /* a comment in resize() */ +static const int inflate_threshold_root = 30; static void __alias_free_mem(struct rcu_head *head) { @@ -516,14 +513,14 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct node *n, rcu_assign_pointer(tn->child[i], n); } +#define MAX_WORK 10 static struct node *resize(struct trie *t, struct tnode *tn) { int i; - int err = 0; struct tnode *old_tn; int inflate_threshold_use; int halve_threshold_use; - int max_resize; + int max_work; if (!tn) return NULL; @@ -538,18 +535,7 @@ static struct node *resize(struct trie *t, struct tnode *tn) } /* One child */ if (tn->empty_children == tnode_child_length(tn) - 1) - for (i = 0; i < tnode_child_length(tn); i++) { - struct node *n; - - n = tn->child[i]; - if (!n) - continue; - - /* compress one level */ - node_set_parent(n, NULL); - tnode_free_safe(tn); - return n; - } + goto one_child; /* * Double as long as the resulting node has a number of * nonempty nodes that are above the threshold. @@ -618,15 +604,17 @@ static struct node *resize(struct trie *t, struct tnode *tn) /* Keep root node larger */ - if (!tn->parent) - inflate_threshold_use = inflate_threshold_root + - inflate_threshold_root_fix; - else + if (!node_parent((struct node*) tn)) { + inflate_threshold_use = inflate_threshold_root; + halve_threshold_use = halve_threshold_root; + } + else { inflate_threshold_use = inflate_threshold; + halve_threshold_use = halve_threshold; + } - err = 0; - max_resize = 10; - while ((tn->full_children > 0 && max_resize-- && + max_work = MAX_WORK; + while ((tn->full_children > 0 && max_work-- && 50 * (tn->full_children + tnode_child_length(tn) - tn->empty_children) >= inflate_threshold_use * tnode_child_length(tn))) { @@ -643,47 +631,19 @@ static struct node *resize(struct trie *t, struct tnode *tn) } } - if (max_resize < 0) { - if (!tn->parent) { - /* - * It was observed that during large updates even - * inflate_threshold_root = 35 might be needed to avoid - * this warning; but it should be temporary, so let's - * try to handle this automatically. - */ - if (inflate_threshold_root_fix < INFLATE_FIX_MAX) - inflate_threshold_root_fix++; - else - pr_warning("Fix inflate_threshold_root." - " Now=%d size=%d bits fix=%d\n", - inflate_threshold_root, tn->bits, - inflate_threshold_root_fix); - } else { - pr_warning("Fix inflate_threshold." - " Now=%d size=%d bits\n", - inflate_threshold, tn->bits); - } - } else if (max_resize > 3 && !tn->parent && inflate_threshold_root_fix) - inflate_threshold_root_fix--; - check_tnode(tn); + /* Return if at least one inflate is run */ + if( max_work != MAX_WORK) + return (struct node *) tn; + /* * Halve as long as the number of empty children in this * node is above threshold. */ - - /* Keep root node larger */ - - if (!tn->parent) - halve_threshold_use = halve_threshold_root; - else - halve_threshold_use = halve_threshold; - - err = 0; - max_resize = 10; - while (tn->bits > 1 && max_resize-- && + max_work = MAX_WORK; + while (tn->bits > 1 && max_work-- && 100 * (tnode_child_length(tn) - tn->empty_children) < halve_threshold_use * tnode_child_length(tn)) { @@ -698,19 +658,10 @@ static struct node *resize(struct trie *t, struct tnode *tn) } } - if (max_resize < 0) { - if (!tn->parent) - pr_warning("Fix halve_threshold_root." - " Now=%d size=%d bits\n", - halve_threshold_root, tn->bits); - else - pr_warning("Fix halve_threshold." - " Now=%d size=%d bits\n", - halve_threshold, tn->bits); - } /* Only one child remains */ - if (tn->empty_children == tnode_child_length(tn) - 1) + if (tn->empty_children == tnode_child_length(tn) - 1) { +one_child: for (i = 0; i < tnode_child_length(tn); i++) { struct node *n; @@ -724,7 +675,7 @@ static struct node *resize(struct trie *t, struct tnode *tn) tnode_free_safe(tn); return n; } - + } return (struct node *) tn; } -- cgit v1.1 From 80a1096bac73ce6e98dbbce10cc00a154460bcbd Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sat, 29 Aug 2009 00:00:35 -0700 Subject: tcp: fix premature termination of FIN_WAIT2 time-wait sockets There is a race condition in the time-wait sockets code that can lead to premature termination of FIN_WAIT2 and, subsequently, to RST generation when the FIN,ACK from the peer finally arrives: Time TCP header 0.000000 30755 > http [SYN] Seq=0 Win=2920 Len=0 MSS=1460 TSV=282912 TSER=0 0.000008 http > 30755 aSYN, ACK] Seq=0 Ack=1 Win=2896 Len=0 MSS=1460 TSV=... 0.136899 HEAD /1b.html?n1Lg=v1 HTTP/1.0 [Packet size limited during capture] 0.136934 HTTP/1.0 200 OK [Packet size limited during capture] 0.136945 http > 30755 [FIN, ACK] Seq=187 Ack=207 Win=2690 Len=0 TSV=270521... 0.136974 30755 > http [ACK] Seq=207 Ack=187 Win=2734 Len=0 TSV=283049 TSER=... 0.177983 30755 > http [ACK] Seq=207 Ack=188 Win=2733 Len=0 TSV=283089 TSER=... 0.238618 30755 > http [FIN, ACK] Seq=207 Ack=188 Win=2733 Len=0 TSV=283151... 0.238625 http > 30755 [RST] Seq=188 Win=0 Len=0 Say twdr->slot = 1 and we are running inet_twdr_hangman and in this instance inet_twdr_do_twkill_work returns 1. At that point we will mark slot 1 and schedule inet_twdr_twkill_work. We will also make twdr->slot = 2. Next, a connection is closed and tcp_time_wait(TCP_FIN_WAIT2, timeo) is called which will create a new FIN_WAIT2 time-wait socket and will place it in the last to be reached slot, i.e. twdr->slot = 1. At this point say inet_twdr_twkill_work will run which will start destroying the time-wait sockets in slot 1, including the just added TCP_FIN_WAIT2 one. To avoid this issue we increment the slot only if all entries in the slot have been purged. This change may delay the slots cleanup by a time-wait death row period but only if the worker thread didn't had the time to run/purge the current slot in the next period (6 seconds with default sysctl settings). However, on such a busy system even without this change we would probably see delays... Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- net/ipv4/inet_timewait_sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 61283f9..13f0781 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -218,8 +218,8 @@ void inet_twdr_hangman(unsigned long data) /* We purged the entire slot, anything left? */ if (twdr->tw_count) need_timer = 1; + twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1)); } - twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1)); if (need_timer) mod_timer(&twdr->tw_timer, jiffies + twdr->period); out: -- cgit v1.1 From 31ce8c71a3bdab12debb5899b1f6dac13e54c71d Mon Sep 17 00:00:00 2001 From: David Ward Date: Sat, 29 Aug 2009 00:04:09 -0700 Subject: ipv6: Update Neighbor Cache when IPv6 RA is received on a router When processing a received IPv6 Router Advertisement, the kernel creates or updates an IPv6 Neighbor Cache entry for the sender -- but presently this does not occur if IPv6 forwarding is enabled (net.ipv6.conf.*.forwarding = 1), or if IPv6 Router Advertisements are not accepted (net.ipv6.conf.*.accept_ra = 0), because in these cases processing of the Router Advertisement has already halted. This patch allows the Neighbor Cache to be updated in these cases, while still avoiding any modification to routes or link parameters. This continues to satisfy RFC 4861, since any entry created in the Neighbor Cache as the result of a received Router Advertisement is still placed in the STALE state. Signed-off-by: David Ward Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 1ba42bd..44b4c87 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1151,10 +1151,6 @@ static void ndisc_router_discovery(struct sk_buff *skb) skb->dev->name); return; } - if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { - in6_dev_put(in6_dev); - return; - } if (!ndisc_parse_options(opt, optlen, &ndopts)) { in6_dev_put(in6_dev); @@ -1163,6 +1159,10 @@ static void ndisc_router_discovery(struct sk_buff *skb) return; } + /* skip route and link configuration on routers */ + if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) + goto skip_linkparms; + #ifdef CONFIG_IPV6_NDISC_NODETYPE /* skip link-specific parameters from interior routers */ if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) @@ -1283,9 +1283,7 @@ skip_defrtr: } } -#ifdef CONFIG_IPV6_NDISC_NODETYPE skip_linkparms: -#endif /* * Process options. @@ -1312,6 +1310,10 @@ skip_linkparms: NEIGH_UPDATE_F_ISROUTER); } + /* skip route and link configuration on routers */ + if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) + goto out; + #ifdef CONFIG_IPV6_ROUTE_INFO if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) { struct nd_opt_hdr *p; -- cgit v1.1 From fd3ae5e8fc5e947a9f151e80a65763a24b6368a9 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Tue, 18 Aug 2009 21:55:59 +0000 Subject: Speed-up pfifo_fast lookup using a private bitmap Maintain a per-qdisc bitmap for pfifo_fast giving availability of skbs for each band. This allows faster lookup for a skb when there are no high priority skbs. Also, it helps in (rare) cases when there are no skbs on the list, where an immediate lookup is faster than iterating through the three bands. Signed-off-by: Krishna Kumar Signed-off-by: David S. Miller --- net/sched/sch_generic.c | 70 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 693df7a..6f7aebd 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -406,18 +406,38 @@ static const u8 prio2band[TC_PRIO_MAX+1] = #define PFIFO_FAST_BANDS 3 -static inline struct sk_buff_head *prio2list(struct sk_buff *skb, - struct Qdisc *qdisc) +/* + * Private data for a pfifo_fast scheduler containing: + * - queues for the three band + * - bitmap indicating which of the bands contain skbs + */ +struct pfifo_fast_priv { + u32 bitmap; + struct sk_buff_head q[PFIFO_FAST_BANDS]; +}; + +/* + * Convert a bitmap to the first band number where an skb is queued, where: + * bitmap=0 means there are no skbs on any band. + * bitmap=1 means there is an skb on band 0. + * bitmap=7 means there are skbs on all 3 bands, etc. + */ +static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0}; + +static inline struct sk_buff_head *band2list(struct pfifo_fast_priv *priv, + int band) { - struct sk_buff_head *list = qdisc_priv(qdisc); - return list + prio2band[skb->priority & TC_PRIO_MAX]; + return priv->q + band; } static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc) { - struct sk_buff_head *list = prio2list(skb, qdisc); + int band = prio2band[skb->priority & TC_PRIO_MAX]; + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); + struct sk_buff_head *list = band2list(priv, band); if (skb_queue_len(list) < qdisc_dev(qdisc)->tx_queue_len) { + priv->bitmap |= (1 << band); qdisc->q.qlen++; return __qdisc_enqueue_tail(skb, qdisc, list); } @@ -427,14 +447,18 @@ static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc) static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc) { - int prio; - struct sk_buff_head *list = qdisc_priv(qdisc); + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); + int band = bitmap2band[priv->bitmap]; - for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { - if (!skb_queue_empty(list + prio)) { - qdisc->q.qlen--; - return __qdisc_dequeue_head(qdisc, list + prio); - } + if (likely(band >= 0)) { + struct sk_buff_head *list = band2list(priv, band); + struct sk_buff *skb = __qdisc_dequeue_head(qdisc, list); + + qdisc->q.qlen--; + if (skb_queue_empty(list)) + priv->bitmap &= ~(1 << band); + + return skb; } return NULL; @@ -442,12 +466,13 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc) static struct sk_buff *pfifo_fast_peek(struct Qdisc* qdisc) { - int prio; - struct sk_buff_head *list = qdisc_priv(qdisc); + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); + int band = bitmap2band[priv->bitmap]; + + if (band >= 0) { + struct sk_buff_head *list = band2list(priv, band); - for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) { - if (!skb_queue_empty(list + prio)) - return skb_peek(list + prio); + return skb_peek(list); } return NULL; @@ -456,11 +481,12 @@ static struct sk_buff *pfifo_fast_peek(struct Qdisc* qdisc) static void pfifo_fast_reset(struct Qdisc* qdisc) { int prio; - struct sk_buff_head *list = qdisc_priv(qdisc); + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) - __qdisc_reset_queue(qdisc, list + prio); + __qdisc_reset_queue(qdisc, band2list(priv, prio)); + priv->bitmap = 0; qdisc->qstats.backlog = 0; qdisc->q.qlen = 0; } @@ -480,17 +506,17 @@ nla_put_failure: static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt) { int prio; - struct sk_buff_head *list = qdisc_priv(qdisc); + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) - skb_queue_head_init(list + prio); + skb_queue_head_init(band2list(priv, prio)); return 0; } static struct Qdisc_ops pfifo_fast_ops __read_mostly = { .id = "pfifo_fast", - .priv_size = PFIFO_FAST_BANDS * sizeof(struct sk_buff_head), + .priv_size = sizeof(struct pfifo_fast_priv), .enqueue = pfifo_fast_enqueue, .dequeue = pfifo_fast_dequeue, .peek = pfifo_fast_peek, -- cgit v1.1 From 9a7030b76ab3c2b23b1629c49b6df33fb5aed305 Mon Sep 17 00:00:00 2001 From: John Dykstra Date: Wed, 19 Aug 2009 09:47:41 +0000 Subject: tcp: Remove redundant copy of MD5 authentication key Remove the copy of the MD5 authentication key from tcp_check_req(). This key has already been copied by tcp_v4_syn_recv_sock() or tcp_v6_syn_recv_sock(). Signed-off-by: John Dykstra Signed-off-by: David S. Miller --- net/ipv4/tcp_minisocks.c | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f8d67cc..6c8b422 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -657,29 +657,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); if (child == NULL) goto listen_overflow; -#ifdef CONFIG_TCP_MD5SIG - else { - /* Copy over the MD5 key from the original socket */ - struct tcp_md5sig_key *key; - struct tcp_sock *tp = tcp_sk(sk); - key = tp->af_specific->md5_lookup(sk, child); - if (key != NULL) { - /* - * We're using one, so create a matching key on the - * newsk structure. If we fail to get memory then we - * end up not copying the key across. Shucks. - */ - char *newkey = kmemdup(key->key, key->keylen, - GFP_ATOMIC); - if (newkey) { - if (!tcp_alloc_md5sig_pool()) - BUG(); - tp->af_specific->md5_add(child, child, newkey, - key->keylen); - } - } - } -#endif inet_csk_reqsk_queue_unlink(sk, req, prev); inet_csk_reqsk_queue_removed(sk, req); -- cgit v1.1 From ea00b8e2223170a842bee06e0f27754ccdf2a217 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 28 Aug 2009 09:57:21 +0000 Subject: can: switch to seq_file create_proc_read_entry() is going to be removed soon. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/can/bcm.c | 85 ++++++++--------- net/can/proc.c | 281 +++++++++++++++++++++++++++------------------------------ 2 files changed, 167 insertions(+), 199 deletions(-) (limited to 'net') diff --git a/net/can/bcm.c b/net/can/bcm.c index 72720c7..597da4f 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -146,23 +147,18 @@ static char *bcm_proc_getifname(int ifindex) return "???"; } -static int bcm_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int bcm_proc_show(struct seq_file *m, void *v) { - int len = 0; - struct sock *sk = (struct sock *)data; + struct sock *sk = (struct sock *)m->private; struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; - len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p", - sk->sk_socket); - len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk); - len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo); - len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu", - bo->dropped_usr_msgs); - len += snprintf(page + len, PAGE_SIZE - len, " / bound %s", - bcm_proc_getifname(bo->ifindex)); - len += snprintf(page + len, PAGE_SIZE - len, " <<<\n"); + seq_printf(m, ">>> socket %p", sk->sk_socket); + seq_printf(m, " / sk %p", sk); + seq_printf(m, " / bo %p", bo); + seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); + seq_printf(m, " / bound %s", bcm_proc_getifname(bo->ifindex)); + seq_printf(m, " <<<\n"); list_for_each_entry(op, &bo->rx_ops, list) { @@ -172,71 +168,62 @@ static int bcm_read_proc(char *page, char **start, off_t off, if (!op->frames_abs) continue; - len += snprintf(page + len, PAGE_SIZE - len, - "rx_op: %03X %-5s ", + seq_printf(m, "rx_op: %03X %-5s ", op->can_id, bcm_proc_getifname(op->ifindex)); - len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ", - op->nframes, + seq_printf(m, "[%d]%c ", op->nframes, (op->flags & RX_CHECK_DLC)?'d':' '); if (op->kt_ival1.tv64) - len += snprintf(page + len, PAGE_SIZE - len, - "timeo=%lld ", + seq_printf(m, "timeo=%lld ", (long long) ktime_to_us(op->kt_ival1)); if (op->kt_ival2.tv64) - len += snprintf(page + len, PAGE_SIZE - len, - "thr=%lld ", + seq_printf(m, "thr=%lld ", (long long) ktime_to_us(op->kt_ival2)); - len += snprintf(page + len, PAGE_SIZE - len, - "# recv %ld (%ld) => reduction: ", + seq_printf(m, "# recv %ld (%ld) => reduction: ", op->frames_filtered, op->frames_abs); reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; - len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n", + seq_printf(m, "%s%ld%%\n", (reduction == 100)?"near ":"", reduction); - - if (len > PAGE_SIZE - 200) { - /* mark output cut off */ - len += snprintf(page + len, PAGE_SIZE - len, "(..)\n"); - break; - } } list_for_each_entry(op, &bo->tx_ops, list) { - len += snprintf(page + len, PAGE_SIZE - len, - "tx_op: %03X %s [%d] ", + seq_printf(m, "tx_op: %03X %s [%d] ", op->can_id, bcm_proc_getifname(op->ifindex), op->nframes); if (op->kt_ival1.tv64) - len += snprintf(page + len, PAGE_SIZE - len, "t1=%lld ", + seq_printf(m, "t1=%lld ", (long long) ktime_to_us(op->kt_ival1)); if (op->kt_ival2.tv64) - len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ", + seq_printf(m, "t2=%lld ", (long long) ktime_to_us(op->kt_ival2)); - len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n", - op->frames_abs); - - if (len > PAGE_SIZE - 100) { - /* mark output cut off */ - len += snprintf(page + len, PAGE_SIZE - len, "(..)\n"); - break; - } + seq_printf(m, "# sent %ld\n", op->frames_abs); } + seq_putc(m, '\n'); + return 0; +} - len += snprintf(page + len, PAGE_SIZE - len, "\n"); - - *eof = 1; - return len; +static int bcm_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm_proc_show, PDE(inode)->data); } +static const struct file_operations bcm_proc_fops = { + .owner = THIS_MODULE, + .open = bcm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op @@ -1515,9 +1502,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, if (proc_dir) { /* unique socket address as filename */ sprintf(bo->procname, "%p", sock); - bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644, - proc_dir, - bcm_read_proc, sk); + bo->bcm_proc_read = proc_create_data(bo->procname, 0644, + proc_dir, + &bcm_proc_fops, sk); } return 0; diff --git a/net/can/proc.c b/net/can/proc.c index 1463653..9b9ad29 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -196,8 +196,8 @@ void can_stat_update(unsigned long data) * */ -static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list, - struct net_device *dev) +static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list, + struct net_device *dev) { struct receiver *r; struct hlist_node *n; @@ -208,199 +208,188 @@ static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list, " %-5s %08X %08x %08x %08x %8ld %s\n" : " %-5s %03X %08x %08lx %08lx %8ld %s\n"; - len += snprintf(page + len, PAGE_SIZE - len, fmt, - DNAME(dev), r->can_id, r->mask, + seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask, (unsigned long)r->func, (unsigned long)r->data, r->matches, r->ident); - - /* does a typical line fit into the current buffer? */ - - /* 100 Bytes before end of buffer */ - if (len > PAGE_SIZE - 100) { - /* mark output cut off */ - len += snprintf(page + len, PAGE_SIZE - len, - " (..)\n"); - break; - } } rcu_read_unlock(); - - return len; } -static int can_print_recv_banner(char *page, int len) +static void can_print_recv_banner(struct seq_file *m) { /* * can1. 00000000 00000000 00000000 * ....... 0 tp20 */ - len += snprintf(page + len, PAGE_SIZE - len, - " device can_id can_mask function" + seq_puts(m, " device can_id can_mask function" " userdata matches ident\n"); - - return len; } -static int can_proc_read_stats(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int can_stats_proc_show(struct seq_file *m, void *v) { - int len = 0; + seq_putc(m, '\n'); + seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames); + seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames); + seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld transmitted frames (TXF)\n", - can_stats.tx_frames); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld received frames (RXF)\n", can_stats.rx_frames); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld matched frames (RXMF)\n", can_stats.matches); - - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); if (can_stattimer.function == can_stat_update) { - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld %% total match ratio (RXMR)\n", + seq_printf(m, " %8ld %% total match ratio (RXMR)\n", can_stats.total_rx_match_ratio); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s total tx rate (TXR)\n", + seq_printf(m, " %8ld frames/s total tx rate (TXR)\n", can_stats.total_tx_rate); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s total rx rate (RXR)\n", + seq_printf(m, " %8ld frames/s total rx rate (RXR)\n", can_stats.total_rx_rate); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld %% current match ratio (CRXMR)\n", + seq_printf(m, " %8ld %% current match ratio (CRXMR)\n", can_stats.current_rx_match_ratio); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s current tx rate (CTXR)\n", + seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n", can_stats.current_tx_rate); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s current rx rate (CRXR)\n", + seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n", can_stats.current_rx_rate); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld %% max match ratio (MRXMR)\n", + seq_printf(m, " %8ld %% max match ratio (MRXMR)\n", can_stats.max_rx_match_ratio); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s max tx rate (MTXR)\n", + seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n", can_stats.max_tx_rate); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld frames/s max rx rate (MRXR)\n", + seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n", can_stats.max_rx_rate); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); } - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld current receive list entries (CRCV)\n", + seq_printf(m, " %8ld current receive list entries (CRCV)\n", can_pstats.rcv_entries); - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld maximum receive list entries (MRCV)\n", + seq_printf(m, " %8ld maximum receive list entries (MRCV)\n", can_pstats.rcv_entries_max); if (can_pstats.stats_reset) - len += snprintf(page + len, PAGE_SIZE - len, - "\n %8ld statistic resets (STR)\n", + seq_printf(m, "\n %8ld statistic resets (STR)\n", can_pstats.stats_reset); if (can_pstats.user_reset) - len += snprintf(page + len, PAGE_SIZE - len, - " %8ld user statistic resets (USTR)\n", + seq_printf(m, " %8ld user statistic resets (USTR)\n", can_pstats.user_reset); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); - - *eof = 1; - return len; + seq_putc(m, '\n'); + return 0; } -static int can_proc_read_reset_stats(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int can_stats_proc_open(struct inode *inode, struct file *file) { - int len = 0; + return single_open(file, can_stats_proc_show, NULL); +} + +static const struct file_operations can_stats_proc_fops = { + .owner = THIS_MODULE, + .open = can_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static int can_reset_stats_proc_show(struct seq_file *m, void *v) +{ user_reset = 1; if (can_stattimer.function == can_stat_update) { - len += snprintf(page + len, PAGE_SIZE - len, - "Scheduled statistic reset #%ld.\n", + seq_printf(m, "Scheduled statistic reset #%ld.\n", can_pstats.stats_reset + 1); } else { if (can_stats.jiffies_init != jiffies) can_init_stats(); - len += snprintf(page + len, PAGE_SIZE - len, - "Performed statistic reset #%ld.\n", + seq_printf(m, "Performed statistic reset #%ld.\n", can_pstats.stats_reset); } + return 0; +} - *eof = 1; - return len; +static int can_reset_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, can_reset_stats_proc_show, NULL); } -static int can_proc_read_version(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations can_reset_stats_proc_fops = { + .owner = THIS_MODULE, + .open = can_reset_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int can_version_proc_show(struct seq_file *m, void *v) { - int len = 0; + seq_printf(m, "%s\n", CAN_VERSION_STRING); + return 0; +} - len += snprintf(page + len, PAGE_SIZE - len, "%s\n", - CAN_VERSION_STRING); - *eof = 1; - return len; +static int can_version_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, can_version_proc_show, NULL); } -static int can_proc_read_rcvlist(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations can_version_proc_fops = { + .owner = THIS_MODULE, + .open = can_version_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int can_rcvlist_proc_show(struct seq_file *m, void *v) { /* double cast to prevent GCC warning */ - int idx = (int)(long)data; - int len = 0; + int idx = (int)(long)m->private; struct dev_rcv_lists *d; struct hlist_node *n; - len += snprintf(page + len, PAGE_SIZE - len, - "\nreceive list '%s':\n", rx_list_name[idx]); + seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]); rcu_read_lock(); hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) { if (!hlist_empty(&d->rx[idx])) { - len = can_print_recv_banner(page, len); - len = can_print_rcvlist(page, len, &d->rx[idx], d->dev); + can_print_recv_banner(m); + can_print_rcvlist(m, &d->rx[idx], d->dev); } else - len += snprintf(page + len, PAGE_SIZE - len, - " (%s: no entry)\n", DNAME(d->dev)); - - /* exit on end of buffer? */ - if (len > PAGE_SIZE - 100) - break; + seq_printf(m, " (%s: no entry)\n", DNAME(d->dev)); } rcu_read_unlock(); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); + return 0; +} - *eof = 1; - return len; +static int can_rcvlist_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, can_rcvlist_proc_show, PDE(inode)->data); } -static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations can_rcvlist_proc_fops = { + .owner = THIS_MODULE, + .open = can_rcvlist_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) { - int len = 0; struct dev_rcv_lists *d; struct hlist_node *n; /* RX_SFF */ - len += snprintf(page + len, PAGE_SIZE - len, - "\nreceive list 'rx_sff':\n"); + seq_puts(m, "\nreceive list 'rx_sff':\n"); rcu_read_lock(); hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) { @@ -413,46 +402,38 @@ static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, } if (!all_empty) { - len = can_print_recv_banner(page, len); + can_print_recv_banner(m); for (i = 0; i < 0x800; i++) { - if (!hlist_empty(&d->rx_sff[i]) && - len < PAGE_SIZE - 100) - len = can_print_rcvlist(page, len, - &d->rx_sff[i], - d->dev); + if (!hlist_empty(&d->rx_sff[i])) + can_print_rcvlist(m, &d->rx_sff[i], + d->dev); } } else - len += snprintf(page + len, PAGE_SIZE - len, - " (%s: no entry)\n", DNAME(d->dev)); - - /* exit on end of buffer? */ - if (len > PAGE_SIZE - 100) - break; + seq_printf(m, " (%s: no entry)\n", DNAME(d->dev)); } rcu_read_unlock(); - len += snprintf(page + len, PAGE_SIZE - len, "\n"); + seq_putc(m, '\n'); + return 0; +} - *eof = 1; - return len; +static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, can_rcvlist_sff_proc_show, NULL); } +static const struct file_operations can_rcvlist_sff_proc_fops = { + .owner = THIS_MODULE, + .open = can_rcvlist_sff_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * proc utility functions */ -static struct proc_dir_entry *can_create_proc_readentry(const char *name, - mode_t mode, - read_proc_t *read_proc, - void *data) -{ - if (can_dir) - return create_proc_read_entry(name, mode, can_dir, read_proc, - data); - else - return NULL; -} - static void can_remove_proc_readentry(const char *name) { if (can_dir) @@ -474,24 +455,24 @@ void can_init_proc(void) } /* own procfs entries from the AF_CAN core */ - pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644, - can_proc_read_version, NULL); - pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644, - can_proc_read_stats, NULL); - pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644, - can_proc_read_reset_stats, NULL); - pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644, - can_proc_read_rcvlist, (void *)RX_ERR); - pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644, - can_proc_read_rcvlist, (void *)RX_ALL); - pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644, - can_proc_read_rcvlist, (void *)RX_FIL); - pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644, - can_proc_read_rcvlist, (void *)RX_INV); - pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644, - can_proc_read_rcvlist, (void *)RX_EFF); - pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644, - can_proc_read_rcvlist_sff, NULL); + pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir, + &can_version_proc_fops); + pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir, + &can_stats_proc_fops); + pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir, + &can_reset_stats_proc_fops); + pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir, + &can_rcvlist_proc_fops, (void *)RX_ERR); + pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir, + &can_rcvlist_proc_fops, (void *)RX_ALL); + pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir, + &can_rcvlist_proc_fops, (void *)RX_FIL); + pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir, + &can_rcvlist_proc_fops, (void *)RX_INV); + pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir, + &can_rcvlist_proc_fops, (void *)RX_EFF); + pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir, + &can_rcvlist_sff_proc_fops); } /* -- cgit v1.1 From b3df9a514f3c2020952cff34bc5bc6694a31c00c Mon Sep 17 00:00:00 2001 From: roel kluin Date: Thu, 27 Aug 2009 02:03:15 +0000 Subject: tipc: fix test of bearer_priority range in tipc_register_media() For the bearer_priority to be less than TIPC_MIN_LINK_PRI and greater than TIPC_MAX_LINK_PRI is logically impossible. Signed-off-by: Roel Kluin Signed-off-by: David S. Miller --- net/tipc/bearer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a7a3677..327011f 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -119,7 +119,7 @@ int tipc_register_media(u32 media_type, warn("Media <%s> rejected, no broadcast address\n", name); goto exit; } - if ((bearer_priority < TIPC_MIN_LINK_PRI) && + if ((bearer_priority < TIPC_MIN_LINK_PRI) || (bearer_priority > TIPC_MAX_LINK_PRI)) { warn("Media <%s> rejected, illegal priority (%u)\n", name, bearer_priority); -- cgit v1.1 From 6ca8b990e07914a87fd1f6dfc5507f5e1c4572e2 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sat, 29 Aug 2009 06:45:09 +0000 Subject: can: use correct NET_RX_ return values Dropped skb's should be documented by an appropriate return value. Use the correct NET_RX_DROP and NET_RX_SUCCESS values for that reason. Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller --- net/can/af_can.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/can/af_can.c b/net/can/af_can.c index f9c027b..ef1c43a 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -686,11 +686,11 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, can_stats.matches_delta++; } - return 0; + return NET_RX_SUCCESS; drop: kfree_skb(skb); - return 0; + return NET_RX_DROP; } /* -- cgit v1.1 From 03a9a447d2dab755b22df79b5e205fdbb9b2c851 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Sat, 29 Aug 2009 20:21:36 +0000 Subject: net: convert remaining non-symbolic return values in dev_queue_xmit Patch compiled and 32 simultaneous netperf testing ran fine. Signed-off-by: Krishna Kumar Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 4b83789..4b33566 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1917,7 +1917,7 @@ gso: HARD_TX_LOCK(dev, txq, cpu); if (!netif_tx_queue_stopped(txq)) { - rc = 0; + rc = NET_XMIT_SUCCESS; if (!dev_hard_start_xmit(skb, dev, txq)) { HARD_TX_UNLOCK(dev, txq); goto out; -- cgit v1.1 From a453e0689a3ccf85c08cb89753d7685046248c5c Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Sun, 30 Aug 2009 22:20:28 -0700 Subject: pkt_sched: Fix resource limiting in pfifo_fast pfifo_fast_enqueue has this check: if (skb_queue_len(list) < qdisc_dev(qdisc)->tx_queue_len) { which allows each band to enqueue upto tx_queue_len skbs for a total of 3*tx_queue_len skbs. I am not sure if this was the intention of limiting in qdisc. Patch compiled and 32 simultaneous netperf testing ran fine. Also: # tc -s qdisc show dev eth2 qdisc pfifo_fast 0: root bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 16835026752 bytes 373116 pkt (dropped 0, overlimits 0 requeues 25) rate 0bit 0pps backlog 0b 0p requeues 25 Signed-off-by: Krishna Kumar Signed-off-by: David S. Miller --- net/sched/sch_generic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6f7aebd..6128e6f 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -432,11 +432,11 @@ static inline struct sk_buff_head *band2list(struct pfifo_fast_priv *priv, static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc) { - int band = prio2band[skb->priority & TC_PRIO_MAX]; - struct pfifo_fast_priv *priv = qdisc_priv(qdisc); - struct sk_buff_head *list = band2list(priv, band); + if (skb_queue_len(&qdisc->q) < qdisc_dev(qdisc)->tx_queue_len) { + int band = prio2band[skb->priority & TC_PRIO_MAX]; + struct pfifo_fast_priv *priv = qdisc_priv(qdisc); + struct sk_buff_head *list = band2list(priv, band); - if (skb_queue_len(list) < qdisc_dev(qdisc)->tx_queue_len) { priv->bitmap |= (1 << band); qdisc->q.qlen++; return __qdisc_enqueue_tail(skb, qdisc, list); -- cgit v1.1 From 1e66dafc75f40a08b2addb82779987b269b4ca23 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 31 Aug 2009 14:18:48 +0200 Subject: ipvs: Use atomic operations atomicly A pointed out by Shin Hong, IPVS doesn't always use atomic operations in an atomic manner. While this seems unlikely to be manifest in strange behaviour, it seems appropriate to clean this up. Cc: shin hong Signed-off-by: Simon Horman Signed-off-by: Patrick McHardy --- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- net/netfilter/ipvs/ip_vs_wrr.c | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index b227750..a986ee2 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1259,7 +1259,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; struct ip_vs_conn *cp; - int ret, restart, af; + int ret, restart, af, pkts; af = (skb->protocol == htons(ETH_P_IP)) ? AF_INET : AF_INET6; @@ -1346,12 +1346,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, * Sync connection if it is about to close to * encorage the standby servers to update the connections timeout */ - atomic_inc(&cp->in_pkts); + pkts = atomic_add_return(1, &cp->in_pkts); if (af == AF_INET && (ip_vs_sync_state & IP_VS_STATE_MASTER) && (((cp->protocol != IPPROTO_TCP || cp->state == IP_VS_TCP_S_ESTABLISHED) && - (atomic_read(&cp->in_pkts) % sysctl_ip_vs_sync_threshold[1] + (pkts % sysctl_ip_vs_sync_threshold[1] == sysctl_ip_vs_sync_threshold[0])) || ((cp->protocol == IPPROTO_TCP) && (cp->old_state != cp->state) && ((cp->state == IP_VS_TCP_S_FIN_WAIT) || diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 70ff82cd..6182e8e 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -77,11 +77,12 @@ static int ip_vs_wrr_gcd_weight(struct ip_vs_service *svc) static int ip_vs_wrr_max_weight(struct ip_vs_service *svc) { struct ip_vs_dest *dest; - int weight = 0; + int new_weight, weight = 0; list_for_each_entry(dest, &svc->destinations, n_list) { - if (atomic_read(&dest->weight) > weight) - weight = atomic_read(&dest->weight); + new_weight = atomic_read(&dest->weight); + if (new_weight > weight) + weight = new_weight; } return weight; -- cgit v1.1 From ee254fa44d902ab89fd0d66851701098f07872a7 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 31 Aug 2009 14:23:15 +0200 Subject: netfilter: nf_conntrack: netns fix re reliable conntrack event delivery Conntracks in netns other than init_net dying list were never killed. Signed-off-by: Alexey Dobriyan Acked-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- net/netfilter/nf_conntrack_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 565c3a8..b371098 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1089,14 +1089,14 @@ void nf_conntrack_flush_report(struct net *net, u32 pid, int report) } EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); -static void nf_ct_release_dying_list(void) +static void nf_ct_release_dying_list(struct net *net) { struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct hlist_nulls_node *n; spin_lock_bh(&nf_conntrack_lock); - hlist_nulls_for_each_entry(h, n, &init_net.ct.dying, hnnode) { + hlist_nulls_for_each_entry(h, n, &net->ct.dying, hnnode) { ct = nf_ct_tuplehash_to_ctrack(h); /* never fails to remove them, no listeners at this point */ nf_ct_kill(ct); @@ -1115,7 +1115,7 @@ static void nf_conntrack_cleanup_net(struct net *net) { i_see_dead_people: nf_ct_iterate_cleanup(net, kill_all, NULL); - nf_ct_release_dying_list(); + nf_ct_release_dying_list(net); if (atomic_read(&net->ct.count) != 0) { schedule(); goto i_see_dead_people; -- cgit v1.1 From 488908696971c5ea1dcc5d13f29c158ba4f6ae7d Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 31 Aug 2009 15:30:31 +0200 Subject: netfilter: ip6t_eui: fix read outside array bounds Use memcmp() instead of open coded comparison that reads one byte past the intended end. Based on patch from Roel Kluin Signed-off-by: Patrick McHardy --- net/ipv6/netfilter/ip6t_eui64.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c index db610ba..ca287f6 100644 --- a/net/ipv6/netfilter/ip6t_eui64.c +++ b/net/ipv6/netfilter/ip6t_eui64.c @@ -23,7 +23,6 @@ static bool eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par) { unsigned char eui64[8]; - int i = 0; if (!(skb_mac_header(skb) >= skb->head && skb_mac_header(skb) + ETH_HLEN <= skb->data) && @@ -42,12 +41,8 @@ eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par) eui64[4] = 0xfe; eui64[0] ^= 0x02; - i = 0; - while (ipv6_hdr(skb)->saddr.s6_addr[8 + i] == eui64[i] - && i < 8) - i++; - - if (i == 8) + if (!memcmp(ipv6_hdr(skb)->saddr.s6_addr + 8, eui64, + sizeof(eui64))) return true; } } -- cgit v1.1 From 94b265514a8398ba3cfecb5a821a027b68a5c38e Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Mon, 31 Aug 2009 16:22:23 +0200 Subject: IPVS: Add handling of incoming ICMPV6 messages Add handling of incoming ICMPv6 messages. This follows the handling of IPv4 ICMP messages. Amongst ther things this problem allows IPVS to behave sensibly when an ICMPV6_PKT_TOOBIG message is received: This message is received when a realserver sends a packet >PMTU to the client. The hop on this path with insufficient MTU will generate an ICMPv6 Packet Too Big message back to the VIP. The LVS server receives this message, but the call to the function handling this has been missing. Thus, IPVS fails to forward the message to the real server, which then does not adjust the path MTU. This patch adds the missing call to ip_vs_in_icmp_v6() in ip_vs_in() to handle this situation. Thanks to Rob Gallagher from HEAnet for reporting this issue and for testing this patch in production (with direct routing mode). [horms@verge.net.au: tweaked changelog] Signed-off-by: Julius Volz Tested-by: Rob Gallagher Signed-off-by: Simon Horman Signed-off-by: Patrick McHardy --- net/netfilter/ipvs/ip_vs_core.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index a986ee2..b95699f 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1277,13 +1277,24 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, return NF_ACCEPT; } - if (unlikely(iph.protocol == IPPROTO_ICMP)) { - int related, verdict = ip_vs_in_icmp(skb, &related, hooknum); +#ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) { + if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { + int related, verdict = ip_vs_in_icmp_v6(skb, &related, hooknum); - if (related) - return verdict; - ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); - } + if (related) + return verdict; + ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); + } + } else +#endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related, verdict = ip_vs_in_icmp(skb, &related, hooknum); + + if (related) + return verdict; + ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); + } /* Protocol supported? */ pp = ip_vs_proto_get(iph.protocol); -- cgit v1.1 From 6fef4c0c8eeff7de13007a5f56113475444a253d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:41 +0000 Subject: netdev: convert pseudo-devices to netdev_tx_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 7 ++++--- net/bluetooth/bnep/netdev.c | 3 ++- net/bridge/br_device.c | 2 +- net/bridge/br_private.h | 3 ++- net/core/pktgen.c | 2 +- net/dsa/dsa_priv.h | 6 +++--- net/dsa/tag_dsa.c | 2 +- net/dsa/tag_edsa.c | 2 +- net/dsa/tag_trailer.c | 2 +- net/ipv4/ip_gre.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ip6mr.c | 3 ++- net/ipv6/sit.c | 3 ++- net/sched/sch_teql.c | 4 ++-- 16 files changed, 26 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 6e695ac..84a2be4 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -288,7 +288,8 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, return rc; } -static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); @@ -325,8 +326,8 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) +static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); u16 vlan_tci; diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 9c42990..26fb831 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -165,7 +165,8 @@ static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session } #endif -static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t bnep_net_xmit(struct sk_buff *skb, + struct net_device *dev) { struct bnep_session *s = netdev_priv(dev); struct sock *sk = s->sock->sk; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 15d43ba..07a0777 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -20,7 +20,7 @@ #include "br_private.h" /* net device transmit always called with no BH (preempt_disabled) */ -int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); const unsigned char *dest = skb->data; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8319247..2114e45 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -143,7 +143,8 @@ static inline int br_is_root_bridge(const struct net_bridge *br) /* br_device.c */ extern void br_dev_setup(struct net_device *dev); -extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); +extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, + struct net_device *dev); /* br_fdb.c */ extern int br_fdb_init(void); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 2001cb1..0bcecbf 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3381,7 +3381,7 @@ static void idle(struct pktgen_dev *pkt_dev) static void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = pkt_dev->odev; - int (*xmit)(struct sk_buff *, struct net_device *) + netdev_tx_t (*xmit)(struct sk_buff *, struct net_device *) = odev->netdev_ops->ndo_start_xmit; struct netdev_queue *txq; u16 queue_map; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 41055f3..4b0ea05 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -169,13 +169,13 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds, int port, char *name); /* tag_dsa.c */ -int dsa_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev); /* tag_edsa.c */ -int edsa_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev); /* tag_trailer.c */ -int trailer_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev); #endif diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 8fa25ba..cdf2d28 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -15,7 +15,7 @@ #define DSA_HLEN 4 -int dsa_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); u8 *dsa_header; diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 815607b..8f53948 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -16,7 +16,7 @@ #define DSA_HLEN 4 #define EDSA_HLEN 8 -int edsa_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); u8 *edsa_header; diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 1c3e30c..a85c829 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -13,7 +13,7 @@ #include #include "dsa_priv.h" -int trailer_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); struct sk_buff *nskb; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index b902ef5..5b1af70 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -662,7 +662,7 @@ drop_nolock: return(0); } -static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->dev->stats; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 98075b6..62548cb 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -387,7 +387,7 @@ static int ipip_rcv(struct sk_buff *skb) * and that skb is filled properly by that function. */ -static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->dev->stats; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 06c33fb..65d421c 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -201,7 +201,7 @@ failure: #ifdef CONFIG_IP_PIMSM -static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { struct net *net = dev_net(dev); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index a1d6045..7d25bbe 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1036,7 +1036,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static int +static netdev_tx_t ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 07ded50..19c8dec 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -416,7 +416,8 @@ static struct inet6_protocol pim6_protocol = { /* Service routines creating virtual interfaces: PIMREG */ -static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, + struct net_device *dev) { struct net *net = dev_net(dev); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 1d25a4d..0ae4f64 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -609,7 +609,8 @@ static inline __be32 try_6to4(struct in6_addr *v6dst) * and that skb is filled properly by that function. */ -static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, + struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct net_device_stats *stats = &tunnel->dev->stats; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 12434b6..5a002c2 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -268,7 +268,7 @@ static inline int teql_resolve(struct sk_buff *skb, return __teql_resolve(skb, skb_res, dev); } -static int teql_master_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) { struct teql_master *master = netdev_priv(dev); struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); @@ -307,7 +307,7 @@ restart: if (!netif_tx_queue_stopped(slave_txq) && !netif_tx_queue_frozen(slave_txq) && - slave_ops->ndo_start_xmit(skb, slave) == 0) { + slave_ops->ndo_start_xmit(skb, slave) == NETDEV_TX_OK) { txq_trans_update(slave_txq); __netif_tx_unlock(slave_txq); master->slaves = NEXT_SLAVE(q); -- cgit v1.1 From 3c805a22a3a178fc5aaadd518afa5358b78bf69e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:42 +0000 Subject: convert ATM drivers to netdev_tx_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/atm/br2684.c | 3 ++- net/atm/clip.c | 3 ++- net/atm/lec.c | 6 ++++-- net/atm/mpc.c | 6 ++++-- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 848af11..d6be0d1 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -223,7 +223,8 @@ static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, return list_empty(&brdev->brvccs) ? NULL : list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */ } -static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct br2684_dev *brdev = BRPRIV(dev); struct br2684_vcc *brvcc; diff --git a/net/atm/clip.c b/net/atm/clip.c index 64910bb..27f6852 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -360,7 +360,8 @@ static int clip_encap(struct atm_vcc *vcc, int mode) return 0; } -static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t clip_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct clip_priv *clip_priv = PRIV(dev); struct atmarp_entry *entry; diff --git a/net/atm/lec.c b/net/atm/lec.c index 8e723c2..b2d6445 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -59,7 +59,8 @@ static unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 }; */ static int lec_open(struct net_device *dev); -static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t lec_start_xmit(struct sk_buff *skb, + struct net_device *dev); static int lec_close(struct net_device *dev); static void lec_init(struct net_device *dev); static struct lec_arp_table *lec_arp_find(struct lec_priv *priv, @@ -247,7 +248,8 @@ static void lec_tx_timeout(struct net_device *dev) netif_wake_queue(dev); } -static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t lec_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct sk_buff *skb2; struct lec_priv *priv = netdev_priv(dev); diff --git a/net/atm/mpc.c b/net/atm/mpc.c index 1ac4b94..38a6cb0 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -73,7 +73,8 @@ static void mpoad_close(struct atm_vcc *vcc); static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb); static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb); -static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t mpc_send_packet(struct sk_buff *skb, + struct net_device *dev); static int mpoa_event_listener(struct notifier_block *mpoa_notifier, unsigned long event, void *dev); static void mpc_timer_refresh(void); static void mpc_cache_check( unsigned long checking_time ); @@ -528,7 +529,8 @@ static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc) /* * Probably needs some error checks and locking, not sure... */ -static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t mpc_send_packet(struct sk_buff *skb, + struct net_device *dev) { struct mpoa_client *mpc; struct ethhdr *eth; -- cgit v1.1 From 36e4d64a82d9a91a73a2b9b32117aedfe2211fb3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:43 +0000 Subject: convert hamradio drivers to netdev_txreturnt_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/netrom/nr_dev.c | 2 +- net/rose/rose_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index c7b7838..7aa11b0 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -169,7 +169,7 @@ static int nr_close(struct net_device *dev) return 0; } -static int nr_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t nr_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; unsigned int len = skb->len; diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index c711e2e..424b893 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -131,7 +131,7 @@ static int rose_close(struct net_device *dev) return 0; } -static int rose_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; -- cgit v1.1 From 6518bbb803fe02b15a3211c8db2afdff0ac4f808 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:50 +0000 Subject: irda: convert to netdev_tx_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/irda/irlan/irlan_eth.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 64230cf..7b6b631 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -41,7 +41,8 @@ static int irlan_eth_open(struct net_device *dev); static int irlan_eth_close(struct net_device *dev); -static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, + struct net_device *dev); static void irlan_eth_set_multicast_list( struct net_device *dev); static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev); @@ -162,7 +163,8 @@ static int irlan_eth_close(struct net_device *dev) * Transmits ethernet frames over IrDA link. * */ -static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, + struct net_device *dev) { struct irlan_cb *self = netdev_priv(dev); int ret; -- cgit v1.1 From 424efe9caf6047ffbcd6b383ff4d2347254aabf1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:51 +0000 Subject: netdev: convert pseudo drivers to netdev_tx_t These are all drivers that don't touch real hardware. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/phonet/pep-gprs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index 4667af5..d183509 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -183,7 +183,7 @@ static int gprs_close(struct net_device *dev) return 0; } -static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev) { struct gprs_dev *gp = netdev_priv(dev); struct sock *sk = gp->sk; -- cgit v1.1 From d0cf9c0dadcdc89a755bcb301cfc9c796eb28ccf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:57 +0000 Subject: wireless: convert drivers to netdev_tx_t Mostly just simple conversions: * ray_cs had bogus return of NET_TX_LOCKED but driver was not using NETIF_F_LLTX * hostap and ipw2x00 had some code that returned value from a called function that also had to change to return netdev_tx_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/mac80211/ieee80211_i.h | 6 ++++-- net/mac80211/tx.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index dbd8411..588005c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1050,8 +1050,10 @@ void ieee80211_recalc_idle(struct ieee80211_local *local); /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); void ieee80211_tx_pending(unsigned long data); -int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); -int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev); /* HT */ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0c08d1e..5143d20 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1483,8 +1483,8 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, dev_put(sdata->dev); } -int ieee80211_monitor_start_xmit(struct sk_buff *skb, - struct net_device *dev) +netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_channel *chan = local->hw.conf.channel; @@ -1568,8 +1568,8 @@ fail: * encapsulated packet will then be passed to master interface, wlan#.11, for * transmission (through low-level driver). */ -int ieee80211_subif_start_xmit(struct sk_buff *skb, - struct net_device *dev) +netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; -- cgit v1.1 From 0af46d997fcbfd25a9e166c2431bfebc03720a36 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Mon, 31 Aug 2009 12:31:55 +0000 Subject: vlan: Add support for net_devices_ops.ndo_fcoe_enable/_disable to VLAN This adds implementation of the net_devices_ops.ndo_fcoe_enable/_disable to the VLAN driver. It checks if the real_dev has support for ndo_fcoe_enable/ ndo_fcoe_disable and if so, passes on to call the associated real_dev. Signed-off-by: Yi Zou Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'net') diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 84a2be4..53f84c7 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -587,6 +587,28 @@ static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) return len; } + +static int vlan_dev_fcoe_enable(struct net_device *dev) +{ + struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + const struct net_device_ops *ops = real_dev->netdev_ops; + int rc = -EINVAL; + + if (ops->ndo_fcoe_enable) + rc = ops->ndo_fcoe_enable(real_dev); + return rc; +} + +static int vlan_dev_fcoe_disable(struct net_device *dev) +{ + struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + const struct net_device_ops *ops = real_dev->netdev_ops; + int rc = -EINVAL; + + if (ops->ndo_fcoe_disable) + rc = ops->ndo_fcoe_disable(real_dev); + return rc; +} #endif static void vlan_dev_change_rx_flags(struct net_device *dev, int change) @@ -750,6 +772,8 @@ static const struct net_device_ops vlan_netdev_ops = { #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, + .ndo_fcoe_enable = vlan_dev_fcoe_enable, + .ndo_fcoe_disable = vlan_dev_fcoe_disable, #endif }; @@ -770,6 +794,8 @@ static const struct net_device_ops vlan_netdev_accel_ops = { #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, + .ndo_fcoe_enable = vlan_dev_fcoe_enable, + .ndo_fcoe_disable = vlan_dev_fcoe_disable, #endif }; -- cgit v1.1 From 6fa382af61338908e5713234bcee598423f661c3 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Mon, 31 Aug 2009 12:33:20 +0000 Subject: dcbnl: Add netlink attributes for setapp/getapp to dcbnl Add defines for dcbnl netlink attributes to support netlink message passing of setapp/getapp in dcbnl. Signed-off-by: Yi Zou Acked-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- net/dcb/dcbnl.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 8379496..4046468 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -64,6 +64,7 @@ static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = { [DCB_ATTR_CAP] = {.type = NLA_NESTED}, [DCB_ATTR_PFC_STATE] = {.type = NLA_U8}, [DCB_ATTR_BCN] = {.type = NLA_NESTED}, + [DCB_ATTR_APP] = {.type = NLA_NESTED}, }; /* DCB priority flow control to User Priority nested attributes */ @@ -158,6 +159,13 @@ static struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = { [DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG}, }; +/* DCB APP nested attributes. */ +static struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = { + [DCB_APP_ATTR_IDTYPE] = {.type = NLA_U8}, + [DCB_APP_ATTR_ID] = {.type = NLA_U16}, + [DCB_APP_ATTR_PRIORITY] = {.type = NLA_U8}, +}; + /* standard netlink reply call */ static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid, u32 seq, u16 flags) -- cgit v1.1 From 579496865cf4ea429146382d62047ffdbaab0dee Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Mon, 31 Aug 2009 12:33:40 +0000 Subject: dcbnl: Add implementations of dcbnl setapp/getapp commands Implements the dcbnl netlink setapp/getapp pair. When a setapp/getapp is received, dcbnl would just pass on to dcbnl_rtnl_op.setapp/getapp that are supposed to be implemented by the low level drivers. Signed-off-by: Yi Zou Acked-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- net/dcb/dcbnl.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) (limited to 'net') diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 4046468..e0879bf 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -544,6 +544,120 @@ static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb, return ret; } +static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb, + u32 pid, u32 seq, u16 flags) +{ + struct sk_buff *dcbnl_skb; + struct nlmsghdr *nlh; + struct dcbmsg *dcb; + struct nlattr *app_nest; + struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; + u16 id; + u8 up, idtype; + int ret = -EINVAL; + + if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->getapp) + goto out; + + ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], + dcbnl_app_nest); + if (ret) + goto out; + + ret = -EINVAL; + /* all must be non-null */ + if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || + (!app_tb[DCB_APP_ATTR_ID])) + goto out; + + /* either by eth type or by socket number */ + idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); + if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && + (idtype != DCB_APP_IDTYPE_PORTNUM)) + goto out; + + id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); + up = netdev->dcbnl_ops->getapp(netdev, idtype, id); + + /* send this back */ + dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!dcbnl_skb) + goto out; + + nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); + dcb = NLMSG_DATA(nlh); + dcb->dcb_family = AF_UNSPEC; + dcb->cmd = DCB_CMD_GAPP; + + app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP); + ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype); + if (ret) + goto out_cancel; + + ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id); + if (ret) + goto out_cancel; + + ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up); + if (ret) + goto out_cancel; + + nla_nest_end(dcbnl_skb, app_nest); + nlmsg_end(dcbnl_skb, nlh); + + ret = rtnl_unicast(dcbnl_skb, &init_net, pid); + if (ret) + goto nlmsg_failure; + + goto out; + +out_cancel: + nla_nest_cancel(dcbnl_skb, app_nest); +nlmsg_failure: + kfree_skb(dcbnl_skb); +out: + return ret; +} + +static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb, + u32 pid, u32 seq, u16 flags) +{ + int ret = -EINVAL; + u16 id; + u8 up, idtype; + struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; + + if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->setapp) + goto out; + + ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], + dcbnl_app_nest); + if (ret) + goto out; + + ret = -EINVAL; + /* all must be non-null */ + if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || + (!app_tb[DCB_APP_ATTR_ID]) || + (!app_tb[DCB_APP_ATTR_PRIORITY])) + goto out; + + /* either by eth type or by socket number */ + idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); + if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && + (idtype != DCB_APP_IDTYPE_PORTNUM)) + goto out; + + id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); + up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]); + + ret = dcbnl_reply(netdev->dcbnl_ops->setapp(netdev, idtype, id, up), + RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP, + pid, seq, flags); +out: + return ret; +} + static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb, u32 pid, u32 seq, u16 flags, int dir) { @@ -1101,6 +1215,14 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq, nlh->nlmsg_flags); goto out; + case DCB_CMD_GAPP: + ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq, + nlh->nlmsg_flags); + goto out; + case DCB_CMD_SAPP: + ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq, + nlh->nlmsg_flags); + goto out; default: goto errout; } -- cgit v1.1 From 4d1a2d9ec1c17df077ed09a0d135bccf5637a3b7 Mon Sep 17 00:00:00 2001 From: Damian Lukowski Date: Wed, 26 Aug 2009 00:16:27 +0000 Subject: Revert Backoff [v3]: Rename skb to icmp_skb in tcp_v4_err() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This supplementary patch renames skb to icmp_skb in tcp_v4_err() in order to disambiguate from another sk_buff variable, which will be introduced in a separate patch. Signed-off-by: Damian Lukowski Acked-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_ipv4.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6d88219..6ca1bc8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -328,26 +328,26 @@ static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu) * */ -void tcp_v4_err(struct sk_buff *skb, u32 info) +void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) { - struct iphdr *iph = (struct iphdr *)skb->data; - struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); + struct iphdr *iph = (struct iphdr *)icmp_skb->data; + struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2)); struct tcp_sock *tp; struct inet_sock *inet; - const int type = icmp_hdr(skb)->type; - const int code = icmp_hdr(skb)->code; + const int type = icmp_hdr(icmp_skb)->type; + const int code = icmp_hdr(icmp_skb)->code; struct sock *sk; __u32 seq; int err; - struct net *net = dev_net(skb->dev); + struct net *net = dev_net(icmp_skb->dev); - if (skb->len < (iph->ihl << 2) + 8) { + if (icmp_skb->len < (iph->ihl << 2) + 8) { ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; } sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest, - iph->saddr, th->source, inet_iif(skb)); + iph->saddr, th->source, inet_iif(icmp_skb)); if (!sk) { ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); return; -- cgit v1.1 From f1ecd5d9e7366609d640ff4040304ea197fbc618 Mon Sep 17 00:00:00 2001 From: Damian Lukowski Date: Wed, 26 Aug 2009 00:16:31 +0000 Subject: Revert Backoff [v3]: Revert RTO on ICMP destination unreachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here, an ICMP host/network unreachable message, whose payload fits to TCP's SND.UNA, is taken as an indication that the RTO retransmission has not been lost due to congestion, but because of a route failure somewhere along the path. With true congestion, a router won't trigger such a message and the patched TCP will operate as standard TCP. This patch reverts one RTO backoff, if an ICMP host/network unreachable message, whose payload fits to TCP's SND.UNA, arrives. Based on the new RTO, the retransmission timer is reset to reflect the remaining time, or - if the revert clocked out the timer - a retransmission is sent out immediately. Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if there have been retransmissions and reversible backoffs, already. Changes from v2: 1) Renaming of skb in tcp_v4_err() moved to another patch. 2) Reintroduced tcp_bound_rto() and __tcp_set_rto(). 3) Fixed code comments. Signed-off-by: Damian Lukowski Acked-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 5 ++--- net/ipv4/tcp_ipv4.c | 37 +++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_timer.c | 2 +- 3 files changed, 40 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2bdb0da..af6d6fa 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -685,7 +685,7 @@ static inline void tcp_set_rto(struct sock *sk) * is invisible. Actually, Linux-2.4 also generates erratic * ACKs in some circumstances. */ - inet_csk(sk)->icsk_rto = (tp->srtt >> 3) + tp->rttvar; + inet_csk(sk)->icsk_rto = __tcp_set_rto(tp); /* 2. Fixups made earlier cannot be right. * If we do not estimate RTO correctly without them, @@ -696,8 +696,7 @@ static inline void tcp_set_rto(struct sock *sk) /* NOTE: clamping at TCP_RTO_MIN is not required, current algo * guarantees that rto is higher. */ - if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX) - inet_csk(sk)->icsk_rto = TCP_RTO_MAX; + tcp_bound_rto(sk); } /* Save metrics learned by this TCP session. diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6ca1bc8..6755e29 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -332,12 +332,15 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) { struct iphdr *iph = (struct iphdr *)icmp_skb->data; struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2)); + struct inet_connection_sock *icsk; struct tcp_sock *tp; struct inet_sock *inet; const int type = icmp_hdr(icmp_skb)->type; const int code = icmp_hdr(icmp_skb)->code; struct sock *sk; + struct sk_buff *skb; __u32 seq; + __u32 remaining; int err; struct net *net = dev_net(icmp_skb->dev); @@ -367,6 +370,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) if (sk->sk_state == TCP_CLOSE) goto out; + icsk = inet_csk(sk); tp = tcp_sk(sk); seq = ntohl(th->seq); if (sk->sk_state != TCP_LISTEN && @@ -393,6 +397,39 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) } err = icmp_err_convert[code].errno; + /* check if icmp_skb allows revert of backoff + * (see draft-zimmermann-tcp-lcd) */ + if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH) + break; + if (seq != tp->snd_una || !icsk->icsk_retransmits || + !icsk->icsk_backoff) + break; + + icsk->icsk_backoff--; + inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) << + icsk->icsk_backoff; + tcp_bound_rto(sk); + + skb = tcp_write_queue_head(sk); + BUG_ON(!skb); + + remaining = icsk->icsk_rto - min(icsk->icsk_rto, + tcp_time_stamp - TCP_SKB_CB(skb)->when); + + if (remaining) { + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + remaining, TCP_RTO_MAX); + } else if (sock_owned_by_user(sk)) { + /* RTO revert clocked out retransmission, + * but socket is locked. Will defer. */ + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + HZ/20, TCP_RTO_MAX); + } else { + /* RTO revert clocked out retransmission. + * Will retransmit now */ + tcp_retransmit_timer(sk); + } + break; case ICMP_TIME_EXCEEDED: err = EHOSTUNREACH; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index c520fb6..408fa4b 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -279,7 +279,7 @@ static void tcp_probe_timer(struct sock *sk) * The TCP retransmit timer. */ -static void tcp_retransmit_timer(struct sock *sk) +void tcp_retransmit_timer(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); -- cgit v1.1 From 6fa12c85031485dff38ce550c24f10da23b0adaa Mon Sep 17 00:00:00 2001 From: Damian Lukowski Date: Wed, 26 Aug 2009 00:16:34 +0000 Subject: Revert Backoff [v3]: Calculate TCP's connection close threshold as a time value. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 1122 specifies two threshold values R1 and R2 for connection timeouts, which may represent a number of allowed retransmissions or a timeout value. Currently linux uses sysctl_tcp_retries{1,2} to specify the thresholds in number of allowed retransmissions. For any desired threshold R2 (by means of time) one can specify tcp_retries2 (by means of number of retransmissions) such that TCP will not time out earlier than R2. This is the case, because the RTO schedule follows a fixed pattern, namely exponential backoff. However, the RTO behaviour is not predictable any more if RTO backoffs can be reverted, as it is the case in the draft "Make TCP more Robust to Long Connectivity Disruptions" (http://tools.ietf.org/html/draft-zimmermann-tcp-lcd). In the worst case TCP would time out a connection after 3.2 seconds, if the initial RTO equaled MIN_RTO and each backoff has been reverted. This patch introduces a function retransmits_timed_out(N), which calculates the timeout of a TCP connection, assuming an initial RTO of MIN_RTO and N unsuccessful, exponentially backed-off retransmissions. Whenever timeout decisions are made by comparing the retransmission counter to some value N, this function can be used, instead. The meaning of tcp_retries2 will be changed, as many more RTO retransmissions can occur than the value indicates. However, it yields a timeout which is similar to the one of an unpatched, exponentially backing off TCP in the same scenario. As no application could rely on an RTO greater than MIN_RTO, there should be no risk of a regression. Signed-off-by: Damian Lukowski Acked-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_timer.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 408fa4b..cdb2ca7 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -137,13 +137,14 @@ static int tcp_write_timeout(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); int retry_until; + bool do_reset; if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { if (icsk->icsk_retransmits) dst_negative_advice(&sk->sk_dst_cache); retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; } else { - if (icsk->icsk_retransmits >= sysctl_tcp_retries1) { + if (retransmits_timed_out(sk, sysctl_tcp_retries1)) { /* Black hole detection */ tcp_mtu_probing(icsk, sk); @@ -155,13 +156,15 @@ static int tcp_write_timeout(struct sock *sk) const int alive = (icsk->icsk_rto < TCP_RTO_MAX); retry_until = tcp_orphan_retries(sk, alive); + do_reset = alive || + !retransmits_timed_out(sk, retry_until); - if (tcp_out_of_resources(sk, alive || icsk->icsk_retransmits < retry_until)) + if (tcp_out_of_resources(sk, do_reset)) return 1; } } - if (icsk->icsk_retransmits >= retry_until) { + if (retransmits_timed_out(sk, retry_until)) { /* Has it gone just too far? */ tcp_write_err(sk); return 1; @@ -385,7 +388,7 @@ void tcp_retransmit_timer(struct sock *sk) out_reset_timer: icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); - if (icsk->icsk_retransmits > sysctl_tcp_retries1) + if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1)) __sk_dst_reset(sk); out:; -- cgit v1.1 From 8a56df0ae1690f8f42a3c6c4532f4b06f93febea Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 1 Sep 2009 14:34:01 +0200 Subject: netfilter: ebt_ulog: fix checkentry return value Commit 19eda87 (netfilter: change return types of check functions for Ebtables extensions) broke the ebtables ulog module by missing a return value conversion. Signed-off-by: Patrick McHardy --- net/bridge/netfilter/ebt_ulog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 133eeae..ce50688 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -266,7 +266,7 @@ static bool ebt_ulog_tg_check(const struct xt_tgchk_param *par) if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN) uloginfo->qthreshold = EBT_ULOG_MAX_QLEN; - return 0; + return true; } static struct xt_target ebt_ulog_tg_reg __read_mostly = { -- cgit v1.1 From 86393e52c3f1e2f6be18383f6ecdbcdc5727d545 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 29 Aug 2009 01:34:49 +0000 Subject: netns: embed ip6_dst_ops directly struct net::ipv6.ip6_dst_ops is separatedly dynamically allocated, but there is no fundamental reason for it. Embed it directly into struct netns_ipv6. For that: * move struct dst_ops into separate header to fix circular dependencies I honestly tried not to, it's pretty impossible to do other way * drop dynamical allocation, allocate together with netns For a change, remove struct dst_ops::dst_net, it's deducible by using container_of() given dst_ops pointer. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/ipv6/route.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 1473ee0..9ccfef3 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -665,7 +665,7 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; - ip6_dst_gc(net->ipv6.ip6_dst_ops); + ip6_dst_gc(&net->ipv6.ip6_dst_ops); net->ipv6.sysctl.ip6_rt_gc_elasticity = saved_rt_elasticity; @@ -970,7 +970,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, if (unlikely(idev == NULL)) return NULL; - rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); + rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); if (unlikely(rt == NULL)) { in6_dev_put(idev); goto out; @@ -1060,7 +1060,7 @@ static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), static int ip6_dst_gc(struct dst_ops *ops) { unsigned long now = jiffies; - struct net *net = ops->dst_net; + struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; @@ -1154,7 +1154,7 @@ int ip6_route_add(struct fib6_config *cfg) goto out; } - rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); + rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); if (rt == NULL) { err = -ENOMEM; @@ -1643,7 +1643,7 @@ out: static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) { struct net *net = dev_net(ort->rt6i_dev); - struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); + struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); if (rt) { rt->u.dst.input = ort->u.dst.input; @@ -1923,7 +1923,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, int anycast) { struct net *net = dev_net(idev->dev); - struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); + struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); struct neighbour *neigh; if (rt == NULL) @@ -2501,7 +2501,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v) net->ipv6.rt6_stats->fib_rt_alloc, net->ipv6.rt6_stats->fib_rt_entries, net->ipv6.rt6_stats->fib_rt_cache, - atomic_read(&net->ipv6.ip6_dst_ops->entries), + atomic_read(&net->ipv6.ip6_dst_ops.entries), net->ipv6.rt6_stats->fib_discarded_routes); return 0; @@ -2637,7 +2637,7 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net) if (table) { table[0].data = &net->ipv6.sysctl.flush_delay; - table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh; + table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; @@ -2655,12 +2655,8 @@ static int ip6_route_net_init(struct net *net) { int ret = -ENOMEM; - net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template, - sizeof(*net->ipv6.ip6_dst_ops), - GFP_KERNEL); - if (!net->ipv6.ip6_dst_ops) - goto out; - net->ipv6.ip6_dst_ops->dst_net = hold_net(net); + memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, + sizeof(net->ipv6.ip6_dst_ops)); net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, sizeof(*net->ipv6.ip6_null_entry), @@ -2669,7 +2665,7 @@ static int ip6_route_net_init(struct net *net) goto out_ip6_dst_ops; net->ipv6.ip6_null_entry->u.dst.path = (struct dst_entry *)net->ipv6.ip6_null_entry; - net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops; + net->ipv6.ip6_null_entry->u.dst.ops = &net->ipv6.ip6_dst_ops; #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, @@ -2679,7 +2675,7 @@ static int ip6_route_net_init(struct net *net) goto out_ip6_null_entry; net->ipv6.ip6_prohibit_entry->u.dst.path = (struct dst_entry *)net->ipv6.ip6_prohibit_entry; - net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops; + net->ipv6.ip6_prohibit_entry->u.dst.ops = &net->ipv6.ip6_dst_ops; net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, sizeof(*net->ipv6.ip6_blk_hole_entry), @@ -2688,7 +2684,7 @@ static int ip6_route_net_init(struct net *net) goto out_ip6_prohibit_entry; net->ipv6.ip6_blk_hole_entry->u.dst.path = (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; - net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops; + net->ipv6.ip6_blk_hole_entry->u.dst.ops = &net->ipv6.ip6_dst_ops; #endif net->ipv6.sysctl.flush_delay = 0; @@ -2717,8 +2713,6 @@ out_ip6_null_entry: kfree(net->ipv6.ip6_null_entry); #endif out_ip6_dst_ops: - release_net(net->ipv6.ip6_dst_ops->dst_net); - kfree(net->ipv6.ip6_dst_ops); goto out; } @@ -2733,8 +2727,6 @@ static void ip6_route_net_exit(struct net *net) kfree(net->ipv6.ip6_prohibit_entry); kfree(net->ipv6.ip6_blk_hole_entry); #endif - release_net(net->ipv6.ip6_dst_ops->dst_net); - kfree(net->ipv6.ip6_dst_ops); } static struct pernet_operations ip6_route_net_ops = { -- cgit v1.1 From 89d69d2b75a8f7e258f4b634cd985374cfd3202e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Sep 2009 11:13:19 +0000 Subject: net: make neigh_ops constant These tables are never modified at runtime. Move to read-only section. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/atm/clip.c | 2 +- net/decnet/dn_neigh.c | 6 +++--- net/ipv4/arp.c | 8 ++++---- net/ipv6/ndisc.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/atm/clip.c b/net/atm/clip.c index 27f6852..64629c3 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -267,7 +267,7 @@ static void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb) kfree_skb(skb); } -static struct neigh_ops clip_neigh_ops = { +static const struct neigh_ops clip_neigh_ops = { .family = AF_INET, .solicit = clip_neigh_solicit, .error_report = clip_neigh_error, diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 923786b..794b5bf 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -59,7 +59,7 @@ static int dn_phase3_output(struct sk_buff *); /* * For talking to broadcast devices: Ethernet & PPP */ -static struct neigh_ops dn_long_ops = { +static const struct neigh_ops dn_long_ops = { .family = AF_DECnet, .error_report = dn_long_error_report, .output = dn_long_output, @@ -71,7 +71,7 @@ static struct neigh_ops dn_long_ops = { /* * For talking to pointopoint and multidrop devices: DDCMP and X.25 */ -static struct neigh_ops dn_short_ops = { +static const struct neigh_ops dn_short_ops = { .family = AF_DECnet, .error_report = dn_short_error_report, .output = dn_short_output, @@ -83,7 +83,7 @@ static struct neigh_ops dn_short_ops = { /* * For talking to DECnet phase III nodes */ -static struct neigh_ops dn_phase3_ops = { +static const struct neigh_ops dn_phase3_ops = { .family = AF_DECnet, .error_report = dn_short_error_report, /* Can use short version here */ .output = dn_phase3_output, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 090e999..4e80f33 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -130,7 +130,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb); static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb); static void parp_redo(struct sk_buff *skb); -static struct neigh_ops arp_generic_ops = { +static const struct neigh_ops arp_generic_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, @@ -140,7 +140,7 @@ static struct neigh_ops arp_generic_ops = { .queue_xmit = dev_queue_xmit, }; -static struct neigh_ops arp_hh_ops = { +static const struct neigh_ops arp_hh_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, @@ -150,7 +150,7 @@ static struct neigh_ops arp_hh_ops = { .queue_xmit = dev_queue_xmit, }; -static struct neigh_ops arp_direct_ops = { +static const struct neigh_ops arp_direct_ops = { .family = AF_INET, .output = dev_queue_xmit, .connected_output = dev_queue_xmit, @@ -158,7 +158,7 @@ static struct neigh_ops arp_direct_ops = { .queue_xmit = dev_queue_xmit, }; -struct neigh_ops arp_broken_ops = { +const struct neigh_ops arp_broken_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 44b4c87..7015478 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -98,7 +98,7 @@ static int pndisc_constructor(struct pneigh_entry *n); static void pndisc_destructor(struct pneigh_entry *n); static void pndisc_redo(struct sk_buff *skb); -static struct neigh_ops ndisc_generic_ops = { +static const struct neigh_ops ndisc_generic_ops = { .family = AF_INET6, .solicit = ndisc_solicit, .error_report = ndisc_error_report, @@ -108,7 +108,7 @@ static struct neigh_ops ndisc_generic_ops = { .queue_xmit = dev_queue_xmit, }; -static struct neigh_ops ndisc_hh_ops = { +static const struct neigh_ops ndisc_hh_ops = { .family = AF_INET6, .solicit = ndisc_solicit, .error_report = ndisc_error_report, @@ -119,7 +119,7 @@ static struct neigh_ops ndisc_hh_ops = { }; -static struct neigh_ops ndisc_direct_ops = { +static const struct neigh_ops ndisc_direct_ops = { .family = AF_INET6, .output = dev_queue_xmit, .connected_output = dev_queue_xmit, -- cgit v1.1 From f2798eb4e01b095f273f4bf40f511c9d69c0e1da Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Sun, 30 Aug 2009 20:10:43 +0000 Subject: drop_monitor: fix trace_napi_poll_hit() The net_dev of backlog napi is NULL, like below: __get_cpu_var(softnet_data).backlog.dev == NULL So, we should check it in napi tracepoint's probe function Acked-by: Neil Horman Signed-off-by: Xiao Guangrong Signed-off-by: David S. Miller --- net/core/drop_monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 9d66fa9..d311202 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -182,7 +182,8 @@ static void trace_napi_poll_hit(struct napi_struct *napi) /* * Ratelimit our check time to dm_hw_check_delta jiffies */ - if (!time_after(jiffies, napi->dev->last_rx + dm_hw_check_delta)) + if (!napi->dev || + !time_after(jiffies, napi->dev->last_rx + dm_hw_check_delta)) return; rcu_read_lock(); -- cgit v1.1 From 0625491493d9000e4556bf566d205c28c8e7dc4e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 1 Sep 2009 18:37:16 -0700 Subject: ipv6: ip6_push_pending_frames() should increment IPSTATS_MIB_OUTDISCARDS qdisc drops should be notified to IP_RECVERR enabled sockets, as done in IPV4. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 6ad5aad..a931229 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1520,6 +1520,7 @@ out: ip6_cork_release(inet, np); return err; error: + IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); goto out; } -- cgit v1.1 From 98147d527a038c4aab599e57323a4e5d727c28a6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Sep 2009 19:25:02 +0000 Subject: net: seq_operations should be const Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv6/ip6mr.c | 2 +- net/key/af_key.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 19c8dec..d2df837 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -204,7 +204,7 @@ static int ip6mr_vif_seq_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations ip6mr_vif_seq_ops = { +static const struct seq_operations ip6mr_vif_seq_ops = { .start = ip6mr_vif_seq_start, .next = ip6mr_vif_seq_next, .stop = ip6mr_vif_seq_stop, diff --git a/net/key/af_key.c b/net/key/af_key.c index dba9abd..7b1e99b 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3705,7 +3705,7 @@ static void pfkey_seq_stop(struct seq_file *f, void *v) read_unlock(&pfkey_table_lock); } -static struct seq_operations pfkey_seq_ops = { +static const struct seq_operations pfkey_seq_ops = { .start = pfkey_seq_start, .next = pfkey_seq_next, .stop = pfkey_seq_stop, -- cgit v1.1 From b2e4b3debc327a5b53d9622e0b1785eea2ea2aad Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Sep 2009 19:25:03 +0000 Subject: tcp: MD5 operations should be const Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv4/tcp_ipv4.c | 4 ++-- net/ipv6/tcp_ipv6.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6755e29..3efbe94 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1195,7 +1195,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = { }; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { +static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .md5_lookup = tcp_v4_reqsk_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, }; @@ -1774,7 +1774,7 @@ struct inet_connection_sock_af_ops ipv4_specific = { }; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_sock_af_ops tcp_sock_ipv4_specific = { +static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, .md5_add = tcp_v4_md5_add_func, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d849dd5..eadbc58 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -78,8 +78,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); static struct inet_connection_sock_af_ops ipv6_mapped; static struct inet_connection_sock_af_ops ipv6_specific; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_sock_af_ops tcp_sock_ipv6_specific; -static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; +static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; +static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; #else static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, struct in6_addr *addr) @@ -894,7 +894,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = { }; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .md5_lookup = tcp_v6_reqsk_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, }; @@ -1780,7 +1780,7 @@ static struct inet_connection_sock_af_ops ipv6_specific = { }; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_sock_af_ops tcp_sock_ipv6_specific = { +static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { .md5_lookup = tcp_v6_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, .md5_add = tcp_v6_md5_add_func, @@ -1812,7 +1812,7 @@ static struct inet_connection_sock_af_ops ipv6_mapped = { }; #ifdef CONFIG_TCP_MD5SIG -static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { +static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, .md5_add = tcp_v6_md5_add_func, -- cgit v1.1 From 3b401a81c0d50ea9c718cf837f62cc2e6e79cc30 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Sep 2009 19:25:04 +0000 Subject: inet: inet_connection_sock_af_ops const The function block inet_connect_sock_af_ops contains no data make it constant. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/dccp/ipv4.c | 2 +- net/dccp/ipv6.c | 8 ++++---- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index a0a36c9..d01c00d 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -880,7 +880,7 @@ discard_and_relse: goto discard_it; } -static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = { +static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = { .queue_xmit = ip_queue_xmit, .send_check = dccp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 3e70faa..64f011c 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -35,8 +35,8 @@ /* The per-net dccp.v6_ctl_sk is used for sending RSTs and ACKs */ -static struct inet_connection_sock_af_ops dccp_ipv6_mapped; -static struct inet_connection_sock_af_ops dccp_ipv6_af_ops; +static const struct inet_connection_sock_af_ops dccp_ipv6_mapped; +static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops; static void dccp_v6_hash(struct sock *sk) { @@ -1055,7 +1055,7 @@ failure: return err; } -static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { +static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { .queue_xmit = inet6_csk_xmit, .send_check = dccp_v6_send_check, .rebuild_header = inet6_sk_rebuild_header, @@ -1076,7 +1076,7 @@ static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { /* * DCCP over IPv4 via INET6 API */ -static struct inet_connection_sock_af_ops dccp_ipv6_mapped = { +static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = { .queue_xmit = ip_queue_xmit, .send_check = dccp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3efbe94..ce7d3b0 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1754,7 +1754,7 @@ int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw) return 0; } -struct inet_connection_sock_af_ops ipv4_specific = { +const struct inet_connection_sock_af_ops ipv4_specific = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index eadbc58..d73617e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -75,8 +75,8 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); -static struct inet_connection_sock_af_ops ipv6_mapped; -static struct inet_connection_sock_af_ops ipv6_specific; +static const struct inet_connection_sock_af_ops ipv6_mapped; +static const struct inet_connection_sock_af_ops ipv6_specific; #ifdef CONFIG_TCP_MD5SIG static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; @@ -1760,7 +1760,7 @@ static int tcp_v6_remember_stamp(struct sock *sk) return 0; } -static struct inet_connection_sock_af_ops ipv6_specific = { +static const struct inet_connection_sock_af_ops ipv6_specific = { .queue_xmit = inet6_csk_xmit, .send_check = tcp_v6_send_check, .rebuild_header = inet6_sk_rebuild_header, @@ -1792,7 +1792,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { * TCP over IPv4 via INET6 API */ -static struct inet_connection_sock_af_ops ipv6_mapped = { +static const struct inet_connection_sock_af_ops ipv6_mapped = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, -- cgit v1.1 From 5ca1b998d33c39819fca2b675d80c4469e705f2d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Sep 2009 19:25:05 +0000 Subject: net: file_operations should be const All instances of file_operations should be const. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv6/ip6mr.c | 4 ++-- net/irda/irnet/irnet_ppp.h | 2 +- net/irda/irproc.c | 14 +++++++------- net/key/af_key.c | 2 +- net/mac80211/rc80211_minstrel_debugfs.c | 2 +- net/mac80211/rc80211_pid_debugfs.c | 2 +- net/xfrm/xfrm_proc.c | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index d2df837..5c8d737 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -217,7 +217,7 @@ static int ip6mr_vif_open(struct inode *inode, struct file *file) sizeof(struct ipmr_vif_iter)); } -static struct file_operations ip6mr_vif_fops = { +static const struct file_operations ip6mr_vif_fops = { .owner = THIS_MODULE, .open = ip6mr_vif_open, .read = seq_read, @@ -341,7 +341,7 @@ static int ipmr_mfc_open(struct inode *inode, struct file *file) sizeof(struct ipmr_mfc_iter)); } -static struct file_operations ip6mr_mfc_fops = { +static const struct file_operations ip6mr_mfc_fops = { .owner = THIS_MODULE, .open = ipmr_mfc_open, .read = seq_read, diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h index d9f8bd4..b5df241 100644 --- a/net/irda/irnet/irnet_ppp.h +++ b/net/irda/irnet/irnet_ppp.h @@ -95,7 +95,7 @@ static int /**************************** VARIABLES ****************************/ /* Filesystem callbacks (to call us) */ -static struct file_operations irnet_device_fops = +static const struct file_operations irnet_device_fops = { .owner = THIS_MODULE, .read = dev_irnet_read, diff --git a/net/irda/irproc.c b/net/irda/irproc.c index 8ff1861..318766e 100644 --- a/net/irda/irproc.c +++ b/net/irda/irproc.c @@ -34,21 +34,21 @@ #include #include -extern struct file_operations discovery_seq_fops; -extern struct file_operations irlap_seq_fops; -extern struct file_operations irlmp_seq_fops; -extern struct file_operations irttp_seq_fops; -extern struct file_operations irias_seq_fops; +extern const struct file_operations discovery_seq_fops; +extern const struct file_operations irlap_seq_fops; +extern const struct file_operations irlmp_seq_fops; +extern const struct file_operations irttp_seq_fops; +extern const struct file_operations irias_seq_fops; struct irda_entry { const char *name; - struct file_operations *fops; + const struct file_operations *fops; }; struct proc_dir_entry *proc_irda; EXPORT_SYMBOL(proc_irda); -static struct irda_entry irda_dirs[] = { +static const struct irda_entry irda_dirs[] = { {"discovery", &discovery_seq_fops}, {"irttp", &irttp_seq_fops}, {"irlmp", &irlmp_seq_fops}, diff --git a/net/key/af_key.c b/net/key/af_key.c index 7b1e99b..4e98193 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3718,7 +3718,7 @@ static int pfkey_seq_open(struct inode *inode, struct file *file) sizeof(struct seq_net_private)); } -static struct file_operations pfkey_proc_ops = { +static const struct file_operations pfkey_proc_ops = { .open = pfkey_seq_open, .read = seq_read, .llseek = seq_lseek, diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 3d72ec5..a715d94 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -139,7 +139,7 @@ minstrel_stats_release(struct inode *inode, struct file *file) return 0; } -static struct file_operations minstrel_stat_fops = { +static const struct file_operations minstrel_stat_fops = { .owner = THIS_MODULE, .open = minstrel_stats_open, .read = minstrel_stats_read, diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index a08a9b5..a59043fb 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -198,7 +198,7 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, #undef RC_PID_PRINT_BUF_SIZE -static struct file_operations rc_pid_fop_events = { +static const struct file_operations rc_pid_fop_events = { .owner = THIS_MODULE, .read = rate_control_pid_events_read, .poll = rate_control_pid_events_poll, diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index a2adb51..fef8db5 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -60,7 +60,7 @@ static int xfrm_statistics_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, xfrm_statistics_seq_show); } -static struct file_operations xfrm_statistics_seq_fops = { +static const struct file_operations xfrm_statistics_seq_fops = { .owner = THIS_MODULE, .open = xfrm_statistics_seq_open, .read = seq_read, -- cgit v1.1 From 253850c10d1f9af46f4870616ed29f38ed39fd15 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 1 Sep 2009 08:22:43 -0700 Subject: wireless: update reg debug kconfig entry Refer to the wireless wiki for more information. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index aea7e68..68c504f 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -40,6 +40,10 @@ config CFG80211_REG_DEBUG default n ---help--- You can enable this if you want to debug regulatory changes. + For more information on cfg80211 regulatory refer to the wireless + wiki: + + http://wireless.kernel.org/en/developers/Regulatory If unsure, say N. -- cgit v1.1 From abd8ea22c23c917f56fefcda8a4468fbc2247d7f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 1 Sep 2009 08:22:46 -0700 Subject: wireless: remove mac80211 rate selection extra menu We can just display this upon enabling mac80211 with an 'if MAC80211 != n' check. Cc: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 9db4ff8..4d5543a 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -13,8 +13,7 @@ config MAC80211 comment "CFG80211 needs to be enabled for MAC80211" depends on CFG80211=n -menu "Rate control algorithm selection" - depends on MAC80211 != n +if MAC80211 != n config MAC80211_RC_PID bool "PID controller based rate control algorithm" if EMBEDDED @@ -61,7 +60,7 @@ config MAC80211_RC_DEFAULT default "pid" if MAC80211_RC_DEFAULT_PID default "" -endmenu +endif config MAC80211_MESH bool "Enable mac80211 mesh networking (pre-802.11s) support" -- cgit v1.1 From fcc6cb0c13555e78c2d47257b6d1b5e59b0c419a Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 1 Sep 2009 18:12:11 -0400 Subject: cfg80211: fix looping soft lockup in find_ie() The find_ie() function uses a size_t for the len parameter, and directly uses len as a loop variable. If any received packets are malformed, it is possible for the decrease of len to overflow, and since the result is unsigned, the loop will not terminate. Change it to a signed int so the loop conditional works for negative values. This fixes the following soft lockup: [38573.102007] BUG: soft lockup - CPU#0 stuck for 61s! [phy0:2230] [38573.102007] Modules linked in: aes_i586 aes_generic fuse af_packet ipt_REJECT xt_tcpudp nf_conntrack_ipv4 nf_defrag_ipv4 xt_state iptable_filter ip_tables x_tables acpi_cpufreq binfmt_misc dm_mirror dm_region_hash dm_log dm_multipath dm_mod kvm_intel kvm uinput i915 arc4 ecb drm snd_hda_codec_idt ath5k snd_hda_intel hid_apple mac80211 usbhid appletouch snd_hda_codec snd_pcm ath cfg80211 snd_timer i2c_algo_bit ohci1394 video snd processor ieee1394 rfkill ehci_hcd sg sky2 backlight snd_page_alloc uhci_hcd joydev output ac thermal button battery sr_mod applesmc cdrom input_polldev evdev unix [last unloaded: scsi_wait_scan] [38573.102007] irq event stamp: 2547724535 [38573.102007] hardirqs last enabled at (2547724534): [] restore_all_notrace+0x0/0x18 [38573.102007] hardirqs last disabled at (2547724535): [] apic_timer_interrupt+0x28/0x34 [38573.102007] softirqs last enabled at (92950144): [] __do_softirq+0x108/0x210 [38573.102007] softirqs last disabled at (92950274): [] _spin_lock_bh+0x14/0x80 [38573.102007] [38573.102007] Pid: 2230, comm: phy0 Tainted: G W (2.6.31-rc7-wl #8) MacBook1,1 [38573.102007] EIP: 0060:[] EFLAGS: 00010292 CPU: 0 [38573.102007] EIP is at cmp_ies+0x30/0x180 [cfg80211] [38573.102007] EAX: 00000082 EBX: 00000000 ECX: ffffffc1 EDX: d8efd014 [38573.102007] ESI: ffffff7c EDI: 0000004d EBP: eee2dc50 ESP: eee2dc3c [38573.102007] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [38573.102007] CR0: 8005003b CR2: d8efd014 CR3: 01694000 CR4: 000026d0 [38573.102007] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 [38573.102007] DR6: ffff0ff0 DR7: 00000400 [38573.102007] Call Trace: [38573.102007] [] cmp_bss+0xed/0x100 [cfg80211] [38573.102007] [] cfg80211_bss_update+0x84/0x410 [cfg80211] [38573.102007] [] cfg80211_inform_bss_frame+0x114/0x180 [cfg80211] [38573.102007] [] ieee80211_bss_info_update+0x4f/0x180 [mac80211] [38573.102007] [] ieee80211_rx_bss_info+0x88/0xf0 [mac80211] [38573.102007] [] ? ieee802_11_parse_elems+0x27/0x30 [mac80211] [38573.102007] [] ieee80211_rx_mgmt_probe_resp+0xa4/0x1c0 [mac80211] [38573.102007] [] ieee80211_sta_rx_queued_mgmt+0x919/0xc50 [mac80211] [38573.102007] [] ? sched_clock+0x27/0xa0 [38573.102007] [] ? sched_clock+0x27/0xa0 [38573.102007] [] ? mark_held_locks+0x60/0x80 [38573.102007] [] ? _spin_unlock_irqrestore+0x55/0x70 [38573.102007] [] ? sub_preempt_count+0x85/0xc0 [38573.102007] [] ? _spin_unlock_irqrestore+0x3e/0x70 [38573.102007] [] ? skb_dequeue+0x4f/0x70 [38573.102007] [] ieee80211_sta_work+0x91/0xb80 [mac80211] [38573.102007] [] ? sched_clock+0x27/0xa0 [38573.102007] [] ? sub_preempt_count+0x85/0xc0 [38573.102007] [] worker_thread+0x18f/0x320 [38573.102007] [] ? worker_thread+0x12e/0x320 [38573.102007] [] ? _spin_unlock_irqrestore+0x55/0x70 [38573.102007] [] ? ieee80211_sta_work+0x0/0xb80 [mac80211] [38573.102007] [] ? autoremove_wake_function+0x0/0x50 [38573.102007] [] ? worker_thread+0x0/0x320 [38573.102007] [] kthread+0x84/0x90 [38573.102007] [] ? kthread+0x0/0x90 [38573.102007] [] kernel_thread_helper+0x7/0x10 Cc: stable@kernel.org Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7043de6..19c5a9a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -141,7 +141,7 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) dev->bss_generation++; } -static u8 *find_ie(u8 num, u8 *ies, size_t len) +static u8 *find_ie(u8 num, u8 *ies, int len) { while (len > 2 && ies[0] != num) { len -= ies[1] + 2; -- cgit v1.1 From 5848cc096a23b80b3d15c27d72299f79caf7c517 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 2 Sep 2009 14:37:45 -0700 Subject: net: drop_monitor: make last_rx timestamp private It was recently pointed out to me that the last_rx field of the net_device structure wasn't updated regularly. In fact only the bonding driver really uses it currently. Since the drop_monitor code relies on the last_rx field to detect drops on recevie in hardware, We need to find a more reliable way to rate limit our drop checks (so that we don't check for drops on every frame recevied, which would be inefficient. This patch makes a last_rx timestamp that is private to the drop monitor code and is updated for every device that we track. Signed-off-by: Neil Horman Signed-off-by: David S. Miller --- net/core/drop_monitor.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index d311202..0a113f2 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -52,6 +52,7 @@ struct per_cpu_dm_data { struct dm_hw_stat_delta { struct net_device *dev; + unsigned long last_rx; struct list_head list; struct rcu_head rcu; unsigned long last_drop_val; @@ -180,18 +181,25 @@ static void trace_napi_poll_hit(struct napi_struct *napi) struct dm_hw_stat_delta *new_stat; /* - * Ratelimit our check time to dm_hw_check_delta jiffies + * Don't check napi structures with no associated device */ - if (!napi->dev || - !time_after(jiffies, napi->dev->last_rx + dm_hw_check_delta)) + if (!napi->dev) return; rcu_read_lock(); list_for_each_entry_rcu(new_stat, &hw_stats_list, list) { + /* + * only add a note to our monitor buffer if: + * 1) this is the dev we received on + * 2) its after the last_rx delta + * 3) our rx_dropped count has gone up + */ if ((new_stat->dev == napi->dev) && + (time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) && (napi->dev->stats.rx_dropped != new_stat->last_drop_val)) { trace_drop_common(NULL, NULL); new_stat->last_drop_val = napi->dev->stats.rx_dropped; + new_stat->last_rx = jiffies; break; } } @@ -287,6 +295,7 @@ static int dropmon_net_event(struct notifier_block *ev_block, goto out; new_stat->dev = dev; + new_stat->last_rx = jiffies; INIT_RCU_HEAD(&new_stat->rcu); spin_lock(&trace_state_lock); list_add_rcu(&new_stat->list, &hw_stats_list); -- cgit v1.1 From 2e59af3dcbdf11635c03f22bfc9706744465d589 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 2 Sep 2009 18:03:00 -0700 Subject: vlan: multiqueue vlan device vlan devices are currently not multi-queue capable. We can do that with a new rtnl_link_ops method, get_tx_queues(), called from rtnl_create_link() This new method gets num_tx_queues/real_num_tx_queues from real device. register_vlan_device() is also handled. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/8021q/vlan.c | 5 +++-- net/8021q/vlan_netlink.c | 20 ++++++++++++++++++++ net/core/rtnetlink.c | 10 +++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index e814794..8836575 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -330,12 +330,13 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } - new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, - vlan_setup); + new_dev = alloc_netdev_mq(sizeof(struct vlan_dev_info), name, + vlan_setup, real_dev->num_tx_queues); if (new_dev == NULL) return -ENOBUFS; + new_dev->real_num_tx_queues = real_dev->real_num_tx_queues; dev_net_set(new_dev, net); /* need 4 bytes for extra VLAN header info, * hope the underlying device can handle it. diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index e9c91dc..343146e 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -100,6 +100,25 @@ static int vlan_changelink(struct net_device *dev, return 0; } +static int vlan_get_tx_queues(struct net *net, + struct nlattr *tb[], + unsigned int *num_tx_queues, + unsigned int *real_num_tx_queues) +{ + struct net_device *real_dev; + + if (!tb[IFLA_LINK]) + return -EINVAL; + + real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK])); + if (!real_dev) + return -ENODEV; + + *num_tx_queues = real_dev->num_tx_queues; + *real_num_tx_queues = real_dev->real_num_tx_queues; + return 0; +} + static int vlan_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -216,6 +235,7 @@ struct rtnl_link_ops vlan_link_ops __read_mostly = { .maxtype = IFLA_VLAN_MAX, .policy = vlan_policy, .priv_size = sizeof(struct vlan_dev_info), + .get_tx_queues = vlan_get_tx_queues, .setup = vlan_setup, .validate = vlan_validate, .newlink = vlan_newlink, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b44775f..bbcba2a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -974,12 +974,20 @@ struct net_device *rtnl_create_link(struct net *net, char *ifname, { int err; struct net_device *dev; + unsigned int num_queues = 1; + unsigned int real_num_queues = 1; + if (ops->get_tx_queues) { + err = ops->get_tx_queues(net, tb, &num_queues, &real_num_queues); + if (err) + goto err; + } err = -ENOMEM; - dev = alloc_netdev(ops->priv_size, ifname, ops->setup); + dev = alloc_netdev_mq(ops->priv_size, ifname, ops->setup, num_queues); if (!dev) goto err; + dev->real_num_tx_queues = real_num_queues; if (strchr(dev->name, '%')) { err = dev_alloc_name(dev, dev->name); if (err < 0) -- cgit v1.1 From 6ce9e7b5fe3195d1ae6e3a0753d4ddcac5cd699e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 2 Sep 2009 18:05:33 -0700 Subject: ip: Report qdisc packet drops Christoph Lameter pointed out that packet drops at qdisc level where not accounted in SNMP counters. Only if application sets IP_RECVERR, drops are reported to user (-ENOBUFS errors) and SNMP counters updated. IP_RECVERR is used to enable extended reliable error message passing, but these are not needed to update system wide SNMP stats. This patch changes things a bit to allow SNMP counters to be updated, regardless of IP_RECVERR being set or not on the socket. Example after an UDP tx flood # netstat -s ... IP: 1487048 outgoing packets dropped ... Udp: ... SndbufErrors: 1487048 send() syscalls, do however still return an OK status, to not break applications. Note : send() manual page explicitly says for -ENOBUFS error : "The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.) " This is not true for IP_RECVERR enabled sockets : a send() syscall that hit a qdisc drop returns an ENOBUFS error. Many thanks to Christoph, David, and last but not least, Alexey ! Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/ip_output.c | 2 +- net/ipv4/raw.c | 9 +++++++-- net/ipv4/udp.c | 12 +++++++++--- net/ipv6/ip6_output.c | 2 +- net/ipv6/raw.c | 4 +++- net/ipv6/udp.c | 12 +++++++++--- 6 files changed, 30 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 7d08210..afae0cb 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1302,7 +1302,7 @@ int ip_push_pending_frames(struct sock *sk) err = ip_local_out(skb); if (err) { if (err > 0) - err = inet->recverr ? net_xmit_errno(err) : 0; + err = net_xmit_errno(err); if (err) goto error; } diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 2979f14..ebb1e58 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -375,7 +375,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output); if (err > 0) - err = inet->recverr ? net_xmit_errno(err) : 0; + err = net_xmit_errno(err); if (err) goto error; out: @@ -386,6 +386,8 @@ error_fault: kfree_skb(skb); error: IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); + if (err == -ENOBUFS && !inet->recverr) + err = 0; return err; } @@ -576,8 +578,11 @@ back_from_confirm: &ipc, &rt, msg->msg_flags); if (err) ip_flush_pending_frames(sk); - else if (!(msg->msg_flags & MSG_MORE)) + else if (!(msg->msg_flags & MSG_MORE)) { err = ip_push_pending_frames(sk); + if (err == -ENOBUFS && !inet->recverr) + err = 0; + } release_sock(sk); } done: diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 29ebb0d..ebaaa7f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -561,12 +561,18 @@ static int udp_push_pending_frames(struct sock *sk) send: err = ip_push_pending_frames(sk); + if (err) { + if (err == -ENOBUFS && !inet->recverr) { + UDP_INC_STATS_USER(sock_net(sk), + UDP_MIB_SNDBUFERRORS, is_udplite); + err = 0; + } + } else + UDP_INC_STATS_USER(sock_net(sk), + UDP_MIB_OUTDATAGRAMS, is_udplite); out: up->len = 0; up->pending = 0; - if (!err) - UDP_INC_STATS_USER(sock_net(sk), - UDP_MIB_OUTDATAGRAMS, is_udplite); return err; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index a931229..cd48801 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1511,7 +1511,7 @@ int ip6_push_pending_frames(struct sock *sk) err = ip6_local_out(skb); if (err) { if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; + err = net_xmit_errno(err); if (err) goto error; } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 5068410..7d675b8 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -642,7 +642,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length, err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output); if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; + err = net_xmit_errno(err); if (err) goto error; out: @@ -653,6 +653,8 @@ error_fault: kfree_skb(skb); error: IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); + if (err == -ENOBUFS && !np->recverr) + err = 0; return err; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 20d2ffc..1640406 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -724,12 +724,18 @@ static int udp_v6_push_pending_frames(struct sock *sk) send: err = ip6_push_pending_frames(sk); + if (err) { + if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { + UDP6_INC_STATS_USER(sock_net(sk), + UDP_MIB_SNDBUFERRORS, is_udplite); + err = 0; + } + } else + UDP6_INC_STATS_USER(sock_net(sk), + UDP_MIB_OUTDATAGRAMS, is_udplite); out: up->len = 0; up->pending = 0; - if (!err) - UDP6_INC_STATS_USER(sock_net(sk), - UDP_MIB_OUTDATAGRAMS, is_udplite); return err; } -- cgit v1.1 From 05c6a8d7a7d778f26d8eb821556988993b766092 Mon Sep 17 00:00:00 2001 From: Ajit Khaparde Date: Wed, 2 Sep 2009 17:02:55 +0000 Subject: net/ethtool: Add support for the ethtool feature to flash firmware image from a specified file. This patch adds support to flash a firmware image to a device using ethtool. The driver gets the filename of the firmware image and flashes the image using the request firmware path. The region "on the chip" to be flashed can be specified by an option. It is upto the device driver to enumerate the region number passed by ethtool, to the region to be flashed. The default behavior is to flash all the regions on the chip. Signed-off-by: Ajit Khaparde Signed-off-by: David S. Miller --- net/core/ethtool.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'net') diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 44e5711..4c12ddb 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -898,6 +898,19 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr, return actor(dev, edata.data); } +static int ethtool_flash_device(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_flash efl; + + if (copy_from_user(&efl, useraddr, sizeof(efl))) + return -EFAULT; + + if (!dev->ethtool_ops->flash_device) + return -EOPNOTSUPP; + + return dev->ethtool_ops->flash_device(dev, &efl); +} + /* The main entry point in this file. Called from net/core/dev.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -1111,6 +1124,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SGRO: rc = ethtool_set_gro(dev, useraddr); break; + case ETHTOOL_FLASHDEV: + rc = ethtool_flash_device(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.1 From aa1330766c49199bdab4d4a9096d98b072df9044 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 2 Sep 2009 23:45:45 -0700 Subject: tcp: replace hard coded GFP_KERNEL with sk_allocation This fixed a lockdep warning which appeared when doing stress memory tests over NFS: inconsistent {RECLAIM_FS-ON-W} -> {IN-RECLAIM_FS-W} usage. page reclaim => nfs_writepage => tcp_sendmsg => lock sk_lock mount_root => nfs_root_data => tcp_close => lock sk_lock => tcp_send_fin => alloc_skb_fclone => page reclaim David raised a concern that if the allocation fails in tcp_send_fin(), and it's GFP_ATOMIC, we are going to yield() (which sleeps) and loop endlessly waiting for the allocation to succeed. But fact is, the original GFP_KERNEL also sleeps. GFP_ATOMIC+yield() looks weird, but it is no worse the implicit sleep inside GFP_KERNEL. Both could loop endlessly under memory pressure. CC: Arnaldo Carvalho de Melo CC: David S. Miller CC: Herbert Xu Signed-off-by: Wu Fengguang Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 10 +++++----- net/ipv4/tcp_ipv4.c | 7 ++++--- net/ipv4/tcp_minisocks.c | 2 +- net/ipv4/tcp_output.c | 5 +++-- net/ipv6/tcp_ipv6.c | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 59f69a6..edeea06 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1839,7 +1839,7 @@ void tcp_close(struct sock *sk, long timeout) /* Unread data was tossed, zap the connection. */ NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); tcp_set_state(sk, TCP_CLOSE); - tcp_send_active_reset(sk, GFP_KERNEL); + tcp_send_active_reset(sk, sk->sk_allocation); } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); @@ -2658,7 +2658,7 @@ void tcp_free_md5sig_pool(void) EXPORT_SYMBOL(tcp_free_md5sig_pool); -static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(void) +static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(struct sock *sk) { int cpu; struct tcp_md5sig_pool **pool; @@ -2671,7 +2671,7 @@ static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(void) struct tcp_md5sig_pool *p; struct crypto_hash *hash; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), sk->sk_allocation); if (!p) goto out_free; *per_cpu_ptr(pool, cpu) = p; @@ -2688,7 +2688,7 @@ out_free: return NULL; } -struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(void) +struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(struct sock *sk) { struct tcp_md5sig_pool **pool; int alloc = 0; @@ -2709,7 +2709,7 @@ retry: if (alloc) { /* we cannot hold spinlock here because this may sleep. */ - struct tcp_md5sig_pool **p = __tcp_alloc_md5sig_pool(); + struct tcp_md5sig_pool **p = __tcp_alloc_md5sig_pool(sk); spin_lock_bh(&tcp_md5sig_pool_lock); if (!p) { tcp_md5sig_users--; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ce7d3b0..0543561 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -886,7 +886,7 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, } sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } - if (tcp_alloc_md5sig_pool() == NULL) { + if (tcp_alloc_md5sig_pool(sk) == NULL) { kfree(newkey); return -ENOMEM; } @@ -1007,8 +1007,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, if (!tcp_sk(sk)->md5sig_info) { struct tcp_sock *tp = tcp_sk(sk); - struct tcp_md5sig_info *p = kzalloc(sizeof(*p), GFP_KERNEL); + struct tcp_md5sig_info *p; + p = kzalloc(sizeof(*p), sk->sk_allocation); if (!p) return -EINVAL; @@ -1016,7 +1017,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } - newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); + newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation); if (!newkey) return -ENOMEM; return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6c8b422..e48c37d 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -322,7 +322,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) if (key != NULL) { memcpy(&tcptw->tw_md5_key, key->key, key->keylen); tcptw->tw_md5_keylen = key->keylen; - if (tcp_alloc_md5sig_pool() == NULL) + if (tcp_alloc_md5sig_pool(sk) == NULL) BUG(); } } while (0); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 4e00442..5200aab 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2135,7 +2135,8 @@ void tcp_send_fin(struct sock *sk) } else { /* Socket is locked, keep trying until memory is available. */ for (;;) { - skb = alloc_skb_fclone(MAX_TCP_HEADER, GFP_KERNEL); + skb = alloc_skb_fclone(MAX_TCP_HEADER, + sk->sk_allocation); if (skb) break; yield(); @@ -2388,7 +2389,7 @@ int tcp_connect(struct sock *sk) sk->sk_wmem_queued += buff->truesize; sk_mem_charge(sk, buff->truesize); tp->packets_out += tcp_skb_pcount(buff); - tcp_transmit_skb(sk, buff, 1, GFP_KERNEL); + tcp_transmit_skb(sk, buff, 1, sk->sk_allocation); /* We change tp->snd_nxt after the tcp_transmit_skb() call * in order to make this packet get counted in tcpOutSegs. diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d73617e..65aecf2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -591,7 +591,7 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer, } sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } - if (tcp_alloc_md5sig_pool() == NULL) { + if (tcp_alloc_md5sig_pool(sk) == NULL) { kfree(newkey); return -ENOMEM; } -- cgit v1.1 From 137742cf9738f1b4784058ff79aec7ca85e769d4 Mon Sep 17 00:00:00 2001 From: Karl Hiramoto Date: Wed, 2 Sep 2009 23:26:39 -0700 Subject: atm/br2684: netif_stop_queue() when atm device busy and netif_wake_queue() when we can send packets again. This patch removes the call to dev_kfree_skb() when the atm device is busy. Calling dev_kfree_skb() causes heavy packet loss then the device is under heavy load, the more correct behavior should be to stop the upper layers, then when the lower device can queue packets again wake the upper layers. Signed-off-by: Karl Hiramoto Signed-off-by: Chas Williams Signed-off-by: David S. Miller --- net/atm/br2684.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/atm/br2684.c b/net/atm/br2684.c index d6be0d1..26a646d 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -69,7 +69,7 @@ struct br2684_vcc { struct net_device *device; /* keep old push, pop functions for chaining */ void (*old_push) (struct atm_vcc * vcc, struct sk_buff * skb); - /* void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); */ + void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); enum br2684_encaps encaps; struct list_head brvccs; #ifdef CONFIG_ATM_BR2684_IPFILTER @@ -142,6 +142,22 @@ static struct net_device *br2684_find_dev(const struct br2684_if_spec *s) return NULL; } +/* chained vcc->pop function. Check if we should wake the netif_queue */ +static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct br2684_vcc *brvcc = BR2684_VCC(vcc); + struct net_device *net_dev = skb->dev; + + pr_debug("br2684_pop(vcc %p ; net_dev %p )\n", vcc, net_dev); + brvcc->old_pop(vcc, skb); + + if (!net_dev) + return; + + if (atm_may_send(vcc, 0)) + netif_wake_queue(net_dev); + +} /* * Send a packet out a particular vcc. Not to useful right now, but paves * the way for multiple vcc's per itf. Returns true if we can send, @@ -200,20 +216,19 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev); - if (!atm_may_send(atmvcc, skb->truesize)) { - /* - * We free this here for now, because we cannot know in a higher - * layer whether the skb pointer it supplied wasn't freed yet. - * Now, it always is. - */ - dev_kfree_skb(skb); - return 0; - } atomic_add(skb->truesize, &sk_atm(atmvcc)->sk_wmem_alloc); ATM_SKB(skb)->atm_options = atmvcc->atm_options; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; atmvcc->send(atmvcc, skb); + + if (!atm_may_send(atmvcc, 0)) { + netif_stop_queue(brvcc->device); + /*check for race with br2684_pop*/ + if (atm_may_send(atmvcc, 0)) + netif_start_queue(brvcc->device); + } + return 1; } @@ -504,8 +519,10 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg) atmvcc->user_back = brvcc; brvcc->encaps = (enum br2684_encaps)be.encaps; brvcc->old_push = atmvcc->push; + brvcc->old_pop = atmvcc->pop; barrier(); atmvcc->push = br2684_push; + atmvcc->pop = br2684_pop; __skb_queue_head_init(&queue); rq = &sk_atm(atmvcc)->sk_receive_queue; -- cgit v1.1 From d1b19dff9159bb88fe839c30a7c071faf4761933 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Sep 2009 01:29:39 -0700 Subject: net: net/core/dev.c cleanups Pure style cleanup patch before surgery :) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 589 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 292 insertions(+), 297 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 4b33566..dd94ae6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -191,7 +191,6 @@ static struct list_head ptype_all __read_mostly; /* Taps */ * semaphore held. */ DEFINE_RWLOCK(dev_base_lock); - EXPORT_SYMBOL(dev_base_lock); #define NETDEV_HASHBITS 8 @@ -248,6 +247,7 @@ static RAW_NOTIFIER_HEAD(netdev_chain); */ DEFINE_PER_CPU(struct softnet_data, softnet_data); +EXPORT_PER_CPU_SYMBOL(softnet_data); #ifdef CONFIG_LOCKDEP /* @@ -381,6 +381,7 @@ void dev_add_pack(struct packet_type *pt) } spin_unlock_bh(&ptype_lock); } +EXPORT_SYMBOL(dev_add_pack); /** * __dev_remove_pack - remove packet handler @@ -418,6 +419,8 @@ void __dev_remove_pack(struct packet_type *pt) out: spin_unlock_bh(&ptype_lock); } +EXPORT_SYMBOL(__dev_remove_pack); + /** * dev_remove_pack - remove packet handler * @pt: packet type declaration @@ -436,6 +439,7 @@ void dev_remove_pack(struct packet_type *pt) synchronize_net(); } +EXPORT_SYMBOL(dev_remove_pack); /****************************************************************************** @@ -499,6 +503,7 @@ int netdev_boot_setup_check(struct net_device *dev) } return 0; } +EXPORT_SYMBOL(netdev_boot_setup_check); /** @@ -591,6 +596,7 @@ struct net_device *__dev_get_by_name(struct net *net, const char *name) } return NULL; } +EXPORT_SYMBOL(__dev_get_by_name); /** * dev_get_by_name - find a device by its name @@ -615,6 +621,7 @@ struct net_device *dev_get_by_name(struct net *net, const char *name) read_unlock(&dev_base_lock); return dev; } +EXPORT_SYMBOL(dev_get_by_name); /** * __dev_get_by_index - find a device by its ifindex @@ -640,6 +647,7 @@ struct net_device *__dev_get_by_index(struct net *net, int ifindex) } return NULL; } +EXPORT_SYMBOL(__dev_get_by_index); /** @@ -664,6 +672,7 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex) read_unlock(&dev_base_lock); return dev; } +EXPORT_SYMBOL(dev_get_by_index); /** * dev_getbyhwaddr - find a device by its hardware address @@ -693,7 +702,6 @@ struct net_device *dev_getbyhwaddr(struct net *net, unsigned short type, char *h return NULL; } - EXPORT_SYMBOL(dev_getbyhwaddr); struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type) @@ -707,7 +715,6 @@ struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type) return NULL; } - EXPORT_SYMBOL(__dev_getfirstbyhwtype); struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type) @@ -721,7 +728,6 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type) rtnl_unlock(); return dev; } - EXPORT_SYMBOL(dev_getfirstbyhwtype); /** @@ -736,7 +742,8 @@ EXPORT_SYMBOL(dev_getfirstbyhwtype); * dev_put to indicate they have finished with it. */ -struct net_device * dev_get_by_flags(struct net *net, unsigned short if_flags, unsigned short mask) +struct net_device *dev_get_by_flags(struct net *net, unsigned short if_flags, + unsigned short mask) { struct net_device *dev, *ret; @@ -752,6 +759,7 @@ struct net_device * dev_get_by_flags(struct net *net, unsigned short if_flags, u read_unlock(&dev_base_lock); return ret; } +EXPORT_SYMBOL(dev_get_by_flags); /** * dev_valid_name - check if name is okay for network device @@ -777,6 +785,7 @@ int dev_valid_name(const char *name) } return 1; } +EXPORT_SYMBOL(dev_valid_name); /** * __dev_alloc_name - allocate a name for a device @@ -870,6 +879,7 @@ int dev_alloc_name(struct net_device *dev, const char *name) strlcpy(dev->name, buf, IFNAMSIZ); return ret; } +EXPORT_SYMBOL(dev_alloc_name); /** @@ -906,8 +916,7 @@ int dev_change_name(struct net_device *dev, const char *newname) err = dev_alloc_name(dev, newname); if (err < 0) return err; - } - else if (__dev_get_by_name(net, newname)) + } else if (__dev_get_by_name(net, newname)) return -EEXIST; else strlcpy(dev->name, newname, IFNAMSIZ); @@ -970,7 +979,7 @@ int dev_set_alias(struct net_device *dev, const char *alias, size_t len) return 0; } - dev->ifalias = krealloc(dev->ifalias, len+1, GFP_KERNEL); + dev->ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL); if (!dev->ifalias) return -ENOMEM; @@ -1006,6 +1015,7 @@ void netdev_state_change(struct net_device *dev) rtmsg_ifinfo(RTM_NEWLINK, dev, 0); } } +EXPORT_SYMBOL(netdev_state_change); void netdev_bonding_change(struct net_device *dev) { @@ -1034,6 +1044,7 @@ void dev_load(struct net *net, const char *name) if (!dev && capable(CAP_SYS_MODULE)) request_module("%s", name); } +EXPORT_SYMBOL(dev_load); /** * dev_open - prepare an interface for use. @@ -1118,6 +1129,7 @@ int dev_open(struct net_device *dev) return ret; } +EXPORT_SYMBOL(dev_open); /** * dev_close - shutdown an interface. @@ -1184,6 +1196,7 @@ int dev_close(struct net_device *dev) return 0; } +EXPORT_SYMBOL(dev_close); /** @@ -1279,6 +1292,7 @@ rollback: raw_notifier_chain_unregister(&netdev_chain, nb); goto unlock; } +EXPORT_SYMBOL(register_netdevice_notifier); /** * unregister_netdevice_notifier - unregister a network notifier block @@ -1299,6 +1313,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb) rtnl_unlock(); return err; } +EXPORT_SYMBOL(unregister_netdevice_notifier); /** * call_netdevice_notifiers - call all network notifier blocks @@ -1321,11 +1336,13 @@ void net_enable_timestamp(void) { atomic_inc(&netstamp_needed); } +EXPORT_SYMBOL(net_enable_timestamp); void net_disable_timestamp(void) { atomic_dec(&netstamp_needed); } +EXPORT_SYMBOL(net_disable_timestamp); static inline void net_timestamp(struct sk_buff *skb) { @@ -1359,7 +1376,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) if ((ptype->dev == dev || !ptype->dev) && (ptype->af_packet_priv == NULL || (struct sock *)ptype->af_packet_priv != skb->sk)) { - struct sk_buff *skb2= skb_clone(skb, GFP_ATOMIC); + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (!skb2) break; @@ -1527,6 +1544,7 @@ out_set_summed: out: return ret; } +EXPORT_SYMBOL(skb_checksum_help); /** * skb_gso_segment - Perform segmentation on skb. @@ -1589,7 +1607,6 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) return segs; } - EXPORT_SYMBOL(skb_gso_segment); /* Take action when hardware reception checksum errors are detected. */ @@ -1755,7 +1772,7 @@ u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb) if (skb_rx_queue_recorded(skb)) { hash = skb_get_rx_queue(skb); - while (unlikely (hash >= dev->real_num_tx_queues)) + while (unlikely(hash >= dev->real_num_tx_queues)) hash -= dev->real_num_tx_queues; return hash; } @@ -1890,7 +1907,7 @@ gso: q = rcu_dereference(txq->qdisc); #ifdef CONFIG_NET_CLS_ACT - skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); + skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS); #endif if (q->enqueue) { rc = __dev_xmit_skb(skb, q, dev, txq); @@ -1946,6 +1963,7 @@ out: rcu_read_unlock_bh(); return rc; } +EXPORT_SYMBOL(dev_queue_xmit); /*======================================================================= @@ -2012,6 +2030,7 @@ enqueue: kfree_skb(skb); return NET_RX_DROP; } +EXPORT_SYMBOL(netif_rx); int netif_rx_ni(struct sk_buff *skb) { @@ -2025,7 +2044,6 @@ int netif_rx_ni(struct sk_buff *skb) return err; } - EXPORT_SYMBOL(netif_rx_ni); static void net_tx_action(struct softirq_action *h) @@ -2358,6 +2376,7 @@ out: rcu_read_unlock(); return ret; } +EXPORT_SYMBOL(netif_receive_skb); /* Network device is going away, flush any packets still pending */ static void flush_backlog(void *arg) @@ -2874,7 +2893,7 @@ softnet_break: goto out; } -static gifconf_func_t * gifconf_list [NPROTO]; +static gifconf_func_t *gifconf_list[NPROTO]; /** * register_gifconf - register a SIOCGIF handler @@ -2885,13 +2904,14 @@ static gifconf_func_t * gifconf_list [NPROTO]; * that is passed must not be freed or reused until it has been replaced * by another handler. */ -int register_gifconf(unsigned int family, gifconf_func_t * gifconf) +int register_gifconf(unsigned int family, gifconf_func_t *gifconf) { if (family >= NPROTO) return -EINVAL; gifconf_list[family] = gifconf; return 0; } +EXPORT_SYMBOL(register_gifconf); /* @@ -3102,7 +3122,7 @@ static int softnet_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n", s->total, s->dropped, s->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ - s->cpu_collision ); + s->cpu_collision); return 0; } @@ -3338,6 +3358,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE); return 0; } +EXPORT_SYMBOL(netdev_set_master); static void dev_change_rx_flags(struct net_device *dev, int flags) { @@ -3416,6 +3437,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc) dev_set_rx_mode(dev); return err; } +EXPORT_SYMBOL(dev_set_promiscuity); /** * dev_set_allmulti - update allmulti count on a device @@ -3459,6 +3481,7 @@ int dev_set_allmulti(struct net_device *dev, int inc) } return 0; } +EXPORT_SYMBOL(dev_set_allmulti); /* * Upload unicast and multicast address lists to device and @@ -4088,6 +4111,7 @@ unsigned dev_get_flags(const struct net_device *dev) return flags; } +EXPORT_SYMBOL(dev_get_flags); /** * dev_change_flags - change device settings @@ -4138,12 +4162,13 @@ int dev_change_flags(struct net_device *dev, unsigned flags) } if (dev->flags & IFF_UP && - ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI | + ((old_flags ^ dev->flags) & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) call_netdevice_notifiers(NETDEV_CHANGE, dev); if ((flags ^ dev->gflags) & IFF_PROMISC) { - int inc = (flags & IFF_PROMISC) ? +1 : -1; + int inc = (flags & IFF_PROMISC) ? 1 : -1; + dev->gflags ^= IFF_PROMISC; dev_set_promiscuity(dev, inc); } @@ -4153,7 +4178,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags) IFF_ALLMULTI is requested not asking us and not reporting. */ if ((flags ^ dev->gflags) & IFF_ALLMULTI) { - int inc = (flags & IFF_ALLMULTI) ? +1 : -1; + int inc = (flags & IFF_ALLMULTI) ? 1 : -1; + dev->gflags ^= IFF_ALLMULTI; dev_set_allmulti(dev, inc); } @@ -4165,6 +4191,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags) return ret; } +EXPORT_SYMBOL(dev_change_flags); /** * dev_set_mtu - Change maximum transfer unit @@ -4198,6 +4225,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) call_netdevice_notifiers(NETDEV_CHANGEMTU, dev); return err; } +EXPORT_SYMBOL(dev_set_mtu); /** * dev_set_mac_address - Change Media Access Control Address @@ -4222,6 +4250,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return err; } +EXPORT_SYMBOL(dev_set_mac_address); /* * Perform the SIOCxIFxxx calls, inside read_lock(dev_base_lock) @@ -4235,56 +4264,56 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm return -ENODEV; switch (cmd) { - case SIOCGIFFLAGS: /* Get interface flags */ - ifr->ifr_flags = (short) dev_get_flags(dev); - return 0; + case SIOCGIFFLAGS: /* Get interface flags */ + ifr->ifr_flags = (short) dev_get_flags(dev); + return 0; - case SIOCGIFMETRIC: /* Get the metric on the interface - (currently unused) */ - ifr->ifr_metric = 0; - return 0; + case SIOCGIFMETRIC: /* Get the metric on the interface + (currently unused) */ + ifr->ifr_metric = 0; + return 0; - case SIOCGIFMTU: /* Get the MTU of a device */ - ifr->ifr_mtu = dev->mtu; - return 0; + case SIOCGIFMTU: /* Get the MTU of a device */ + ifr->ifr_mtu = dev->mtu; + return 0; - case SIOCGIFHWADDR: - if (!dev->addr_len) - memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); - else - memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); - ifr->ifr_hwaddr.sa_family = dev->type; - return 0; + case SIOCGIFHWADDR: + if (!dev->addr_len) + memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); + else + memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, + min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + ifr->ifr_hwaddr.sa_family = dev->type; + return 0; - case SIOCGIFSLAVE: - err = -EINVAL; - break; + case SIOCGIFSLAVE: + err = -EINVAL; + break; - case SIOCGIFMAP: - ifr->ifr_map.mem_start = dev->mem_start; - ifr->ifr_map.mem_end = dev->mem_end; - ifr->ifr_map.base_addr = dev->base_addr; - ifr->ifr_map.irq = dev->irq; - ifr->ifr_map.dma = dev->dma; - ifr->ifr_map.port = dev->if_port; - return 0; + case SIOCGIFMAP: + ifr->ifr_map.mem_start = dev->mem_start; + ifr->ifr_map.mem_end = dev->mem_end; + ifr->ifr_map.base_addr = dev->base_addr; + ifr->ifr_map.irq = dev->irq; + ifr->ifr_map.dma = dev->dma; + ifr->ifr_map.port = dev->if_port; + return 0; - case SIOCGIFINDEX: - ifr->ifr_ifindex = dev->ifindex; - return 0; + case SIOCGIFINDEX: + ifr->ifr_ifindex = dev->ifindex; + return 0; - case SIOCGIFTXQLEN: - ifr->ifr_qlen = dev->tx_queue_len; - return 0; + case SIOCGIFTXQLEN: + ifr->ifr_qlen = dev->tx_queue_len; + return 0; - default: - /* dev_ioctl() should ensure this case - * is never reached - */ - WARN_ON(1); - err = -EINVAL; - break; + default: + /* dev_ioctl() should ensure this case + * is never reached + */ + WARN_ON(1); + err = -EINVAL; + break; } return err; @@ -4305,92 +4334,91 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) ops = dev->netdev_ops; switch (cmd) { - case SIOCSIFFLAGS: /* Set interface flags */ - return dev_change_flags(dev, ifr->ifr_flags); - - case SIOCSIFMETRIC: /* Set the metric on the interface - (currently unused) */ - return -EOPNOTSUPP; - - case SIOCSIFMTU: /* Set the MTU of a device */ - return dev_set_mtu(dev, ifr->ifr_mtu); + case SIOCSIFFLAGS: /* Set interface flags */ + return dev_change_flags(dev, ifr->ifr_flags); - case SIOCSIFHWADDR: - return dev_set_mac_address(dev, &ifr->ifr_hwaddr); + case SIOCSIFMETRIC: /* Set the metric on the interface + (currently unused) */ + return -EOPNOTSUPP; - case SIOCSIFHWBROADCAST: - if (ifr->ifr_hwaddr.sa_family != dev->type) - return -EINVAL; - memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - return 0; + case SIOCSIFMTU: /* Set the MTU of a device */ + return dev_set_mtu(dev, ifr->ifr_mtu); - case SIOCSIFMAP: - if (ops->ndo_set_config) { - if (!netif_device_present(dev)) - return -ENODEV; - return ops->ndo_set_config(dev, &ifr->ifr_map); - } - return -EOPNOTSUPP; + case SIOCSIFHWADDR: + return dev_set_mac_address(dev, &ifr->ifr_hwaddr); - case SIOCADDMULTI: - if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) || - ifr->ifr_hwaddr.sa_family != AF_UNSPEC) - return -EINVAL; - if (!netif_device_present(dev)) - return -ENODEV; - return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data, - dev->addr_len, 1); + case SIOCSIFHWBROADCAST: + if (ifr->ifr_hwaddr.sa_family != dev->type) + return -EINVAL; + memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, + min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + return 0; - case SIOCDELMULTI: - if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) || - ifr->ifr_hwaddr.sa_family != AF_UNSPEC) - return -EINVAL; + case SIOCSIFMAP: + if (ops->ndo_set_config) { if (!netif_device_present(dev)) return -ENODEV; - return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data, - dev->addr_len, 1); + return ops->ndo_set_config(dev, &ifr->ifr_map); + } + return -EOPNOTSUPP; - case SIOCSIFTXQLEN: - if (ifr->ifr_qlen < 0) - return -EINVAL; - dev->tx_queue_len = ifr->ifr_qlen; - return 0; + case SIOCADDMULTI: + if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data, + dev->addr_len, 1); + + case SIOCDELMULTI: + if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data, + dev->addr_len, 1); - case SIOCSIFNAME: - ifr->ifr_newname[IFNAMSIZ-1] = '\0'; - return dev_change_name(dev, ifr->ifr_newname); + case SIOCSIFTXQLEN: + if (ifr->ifr_qlen < 0) + return -EINVAL; + dev->tx_queue_len = ifr->ifr_qlen; + return 0; - /* - * Unknown or private ioctl - */ + case SIOCSIFNAME: + ifr->ifr_newname[IFNAMSIZ-1] = '\0'; + return dev_change_name(dev, ifr->ifr_newname); - default: - if ((cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) || - cmd == SIOCBONDENSLAVE || - cmd == SIOCBONDRELEASE || - cmd == SIOCBONDSETHWADDR || - cmd == SIOCBONDSLAVEINFOQUERY || - cmd == SIOCBONDINFOQUERY || - cmd == SIOCBONDCHANGEACTIVE || - cmd == SIOCGMIIPHY || - cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG || - cmd == SIOCBRADDIF || - cmd == SIOCBRDELIF || - cmd == SIOCSHWTSTAMP || - cmd == SIOCWANDEV) { - err = -EOPNOTSUPP; - if (ops->ndo_do_ioctl) { - if (netif_device_present(dev)) - err = ops->ndo_do_ioctl(dev, ifr, cmd); - else - err = -ENODEV; - } - } else - err = -EINVAL; + /* + * Unknown or private ioctl + */ + default: + if ((cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) || + cmd == SIOCBONDENSLAVE || + cmd == SIOCBONDRELEASE || + cmd == SIOCBONDSETHWADDR || + cmd == SIOCBONDSLAVEINFOQUERY || + cmd == SIOCBONDINFOQUERY || + cmd == SIOCBONDCHANGEACTIVE || + cmd == SIOCGMIIPHY || + cmd == SIOCGMIIREG || + cmd == SIOCSMIIREG || + cmd == SIOCBRADDIF || + cmd == SIOCBRDELIF || + cmd == SIOCSHWTSTAMP || + cmd == SIOCWANDEV) { + err = -EOPNOTSUPP; + if (ops->ndo_do_ioctl) { + if (netif_device_present(dev)) + err = ops->ndo_do_ioctl(dev, ifr, cmd); + else + err = -ENODEV; + } + } else + err = -EINVAL; } return err; @@ -4447,135 +4475,135 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) */ switch (cmd) { - /* - * These ioctl calls: - * - can be done by all. - * - atomic and do not require locking. - * - return a value - */ - case SIOCGIFFLAGS: - case SIOCGIFMETRIC: - case SIOCGIFMTU: - case SIOCGIFHWADDR: - case SIOCGIFSLAVE: - case SIOCGIFMAP: - case SIOCGIFINDEX: - case SIOCGIFTXQLEN: - dev_load(net, ifr.ifr_name); - read_lock(&dev_base_lock); - ret = dev_ifsioc_locked(net, &ifr, cmd); - read_unlock(&dev_base_lock); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; + /* + * These ioctl calls: + * - can be done by all. + * - atomic and do not require locking. + * - return a value + */ + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFHWADDR: + case SIOCGIFSLAVE: + case SIOCGIFMAP: + case SIOCGIFINDEX: + case SIOCGIFTXQLEN: + dev_load(net, ifr.ifr_name); + read_lock(&dev_base_lock); + ret = dev_ifsioc_locked(net, &ifr, cmd); + read_unlock(&dev_base_lock); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; - case SIOCETHTOOL: - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ethtool(net, &ifr); - rtnl_unlock(); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; + case SIOCETHTOOL: + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ethtool(net, &ifr); + rtnl_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; - /* - * These ioctl calls: - * - require superuser power. - * - require strict serialization. - * - return a value - */ - case SIOCGMIIPHY: - case SIOCGMIIREG: - case SIOCSIFNAME: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ifsioc(net, &ifr, cmd); - rtnl_unlock(); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - return a value + */ + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSIFNAME: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ifsioc(net, &ifr, cmd); + rtnl_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; - /* - * These ioctl calls: - * - require superuser power. - * - require strict serialization. - * - do not return a value - */ - case SIOCSIFFLAGS: - case SIOCSIFMETRIC: - case SIOCSIFMTU: - case SIOCSIFMAP: - case SIOCSIFHWADDR: - case SIOCSIFSLAVE: - case SIOCADDMULTI: - case SIOCDELMULTI: - case SIOCSIFHWBROADCAST: - case SIOCSIFTXQLEN: - case SIOCSMIIREG: - case SIOCBONDENSLAVE: - case SIOCBONDRELEASE: - case SIOCBONDSETHWADDR: - case SIOCBONDCHANGEACTIVE: - case SIOCBRADDIF: - case SIOCBRDELIF: - case SIOCSHWTSTAMP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* fall through */ - case SIOCBONDSLAVEINFOQUERY: - case SIOCBONDINFOQUERY: + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - do not return a value + */ + case SIOCSIFFLAGS: + case SIOCSIFMETRIC: + case SIOCSIFMTU: + case SIOCSIFMAP: + case SIOCSIFHWADDR: + case SIOCSIFSLAVE: + case SIOCADDMULTI: + case SIOCDELMULTI: + case SIOCSIFHWBROADCAST: + case SIOCSIFTXQLEN: + case SIOCSMIIREG: + case SIOCBONDENSLAVE: + case SIOCBONDRELEASE: + case SIOCBONDSETHWADDR: + case SIOCBONDCHANGEACTIVE: + case SIOCBRADDIF: + case SIOCBRDELIF: + case SIOCSHWTSTAMP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* fall through */ + case SIOCBONDSLAVEINFOQUERY: + case SIOCBONDINFOQUERY: + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ifsioc(net, &ifr, cmd); + rtnl_unlock(); + return ret; + + case SIOCGIFMEM: + /* Get the per device memory space. We can add this but + * currently do not support it */ + case SIOCSIFMEM: + /* Set the per device memory buffer space. + * Not applicable in our case */ + case SIOCSIFLINK: + return -EINVAL; + + /* + * Unknown or private ioctl. + */ + default: + if (cmd == SIOCWANDEV || + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { dev_load(net, ifr.ifr_name); rtnl_lock(); ret = dev_ifsioc(net, &ifr, cmd); rtnl_unlock(); + if (!ret && copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; return ret; - - case SIOCGIFMEM: - /* Get the per device memory space. We can add this but - * currently do not support it */ - case SIOCSIFMEM: - /* Set the per device memory buffer space. - * Not applicable in our case */ - case SIOCSIFLINK: - return -EINVAL; - - /* - * Unknown or private ioctl. - */ - default: - if (cmd == SIOCWANDEV || - (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15)) { - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ifsioc(net, &ifr, cmd); - rtnl_unlock(); - if (!ret && copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - return ret; - } - /* Take care of Wireless Extensions */ - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) - return wext_handle_ioctl(net, &ifr, cmd, arg); - return -EINVAL; + } + /* Take care of Wireless Extensions */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) + return wext_handle_ioctl(net, &ifr, cmd, arg); + return -EINVAL; } } @@ -4840,6 +4868,7 @@ err_uninit: dev->netdev_ops->ndo_uninit(dev); goto out; } +EXPORT_SYMBOL(register_netdevice); /** * init_dummy_netdev - init a dummy network device for NAPI @@ -5126,6 +5155,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, } dev = PTR_ALIGN(p, NETDEV_ALIGN); + pr_err("%s dev=%p queue_count=%d tx=%p\n", name, dev, queue_count, tx); + WARN_ON(queue_count == 1); dev->padded = (char *)dev - (char *)p; if (dev_addr_init(dev)) @@ -5192,6 +5223,7 @@ void free_netdev(struct net_device *dev) /* will free via device release */ put_device(&dev->dev); } +EXPORT_SYMBOL(free_netdev); /** * synchronize_net - Synchronize with packet receive processing @@ -5204,6 +5236,7 @@ void synchronize_net(void) might_sleep(); synchronize_rcu(); } +EXPORT_SYMBOL(synchronize_net); /** * unregister_netdevice - remove device from the kernel @@ -5224,6 +5257,7 @@ void unregister_netdevice(struct net_device *dev) /* Finish processing unregister after unlock */ net_set_todo(dev); } +EXPORT_SYMBOL(unregister_netdevice); /** * unregister_netdev - remove device from the kernel @@ -5242,7 +5276,6 @@ void unregister_netdev(struct net_device *dev) unregister_netdevice(dev); rtnl_unlock(); } - EXPORT_SYMBOL(unregister_netdev); /** @@ -5432,7 +5465,7 @@ unsigned long netdev_increment_features(unsigned long all, unsigned long one, unsigned long mask) { /* If device needs checksumming, downgrade to it. */ - if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM)) + if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM)) all ^= NETIF_F_NO_CSUM | (one & NETIF_F_ALL_CSUM); else if (mask & NETIF_F_ALL_CSUM) { /* If one device supports v4/v6 checksumming, set for all. */ @@ -5658,41 +5691,3 @@ static int __init initialize_hashrnd(void) late_initcall_sync(initialize_hashrnd); -EXPORT_SYMBOL(__dev_get_by_index); -EXPORT_SYMBOL(__dev_get_by_name); -EXPORT_SYMBOL(__dev_remove_pack); -EXPORT_SYMBOL(dev_valid_name); -EXPORT_SYMBOL(dev_add_pack); -EXPORT_SYMBOL(dev_alloc_name); -EXPORT_SYMBOL(dev_close); -EXPORT_SYMBOL(dev_get_by_flags); -EXPORT_SYMBOL(dev_get_by_index); -EXPORT_SYMBOL(dev_get_by_name); -EXPORT_SYMBOL(dev_open); -EXPORT_SYMBOL(dev_queue_xmit); -EXPORT_SYMBOL(dev_remove_pack); -EXPORT_SYMBOL(dev_set_allmulti); -EXPORT_SYMBOL(dev_set_promiscuity); -EXPORT_SYMBOL(dev_change_flags); -EXPORT_SYMBOL(dev_set_mtu); -EXPORT_SYMBOL(dev_set_mac_address); -EXPORT_SYMBOL(free_netdev); -EXPORT_SYMBOL(netdev_boot_setup_check); -EXPORT_SYMBOL(netdev_set_master); -EXPORT_SYMBOL(netdev_state_change); -EXPORT_SYMBOL(netif_receive_skb); -EXPORT_SYMBOL(netif_rx); -EXPORT_SYMBOL(register_gifconf); -EXPORT_SYMBOL(register_netdevice); -EXPORT_SYMBOL(register_netdevice_notifier); -EXPORT_SYMBOL(skb_checksum_help); -EXPORT_SYMBOL(synchronize_net); -EXPORT_SYMBOL(unregister_netdevice); -EXPORT_SYMBOL(unregister_netdevice_notifier); -EXPORT_SYMBOL(net_enable_timestamp); -EXPORT_SYMBOL(net_disable_timestamp); -EXPORT_SYMBOL(dev_get_flags); - -EXPORT_SYMBOL(dev_load); - -EXPORT_PER_CPU_SYMBOL(softnet_data); -- cgit v1.1 From 2f8bc32b7a08502a79e0ccec8697000f2977f2fd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Sep 2009 02:19:58 -0700 Subject: vlan: enable multiqueue xmits vlan_dev_hard_start_xmit() & vlan_dev_hwaccel_hard_start_xmit() select txqueue number 0, instead of using index provided by skb_get_queue_mapping(). This is not correct after commit 2e59af3dcbdf11635c03f [vlan: multiqueue vlan device] because txq->tx_packets & txq->tx_bytes changes are performed on a single location, and not the right locking. Fix is to take the appropriate struct netdev_queue pointer Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 53f84c7..3938c3e 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -291,7 +291,8 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); + int i = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); /* Handle non-VLAN frames if they are sent to us, for example by DHCP. @@ -329,7 +330,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); + int i = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); u16 vlan_tci; vlan_tci = vlan_dev_info(dev)->vlan_id; -- cgit v1.1 From 55f9d6786de2f9cf37db50dbe8ae16f887f3ad7f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Sep 2009 05:17:20 -0700 Subject: net: Remove debugging code Remove a debugging aid I accidently left in previous 'cleanup' patch Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index dd94ae6..1a6561b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5155,8 +5155,6 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, } dev = PTR_ALIGN(p, NETDEV_ALIGN); - pr_err("%s dev=%p queue_count=%d tx=%p\n", name, dev, queue_count, tx); - WARN_ON(queue_count == 1); dev->padded = (char *)dev - (char *)p; if (dev_addr_init(dev)) -- cgit v1.1 From 1a123a3168566b10f87f228ae963770b26f27420 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Sep 2009 00:39:16 +0000 Subject: vlan: adds drops accounting Its hard to tell if vlans are dropping frames, since every frame given to vlan_???_start_xmit() functions is accounted as fully transmitted by lower device. We can test dev_queue_xmit() return values to properly account for dropped frames. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 3938c3e..4198ec5 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -294,6 +294,8 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, int i = skb_get_queue_mapping(skb); struct netdev_queue *txq = netdev_get_tx_queue(dev, i); struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); + unsigned int len; + int ret; /* Handle non-VLAN frames if they are sent to us, for example by DHCP. * @@ -319,11 +321,17 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, vlan_dev_info(dev)->cnt_inc_headroom_on_tx++; } - txq->tx_packets++; - txq->tx_bytes += skb->len; skb->dev = vlan_dev_info(dev)->real_dev; - dev_queue_xmit(skb); + len = skb->len; + ret = dev_queue_xmit(skb); + + if (likely(ret == NET_XMIT_SUCCESS)) { + txq->tx_packets++; + txq->tx_bytes += len; + } else + txq->tx_dropped++; + return NETDEV_TX_OK; } @@ -333,16 +341,23 @@ static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, int i = skb_get_queue_mapping(skb); struct netdev_queue *txq = netdev_get_tx_queue(dev, i); u16 vlan_tci; + unsigned int len; + int ret; vlan_tci = vlan_dev_info(dev)->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); skb = __vlan_hwaccel_put_tag(skb, vlan_tci); - txq->tx_packets++; - txq->tx_bytes += skb->len; - skb->dev = vlan_dev_info(dev)->real_dev; - dev_queue_xmit(skb); + len = skb->len; + ret = dev_queue_xmit(skb); + + if (likely(ret == NET_XMIT_SUCCESS)) { + txq->tx_packets++; + txq->tx_bytes += len; + } else + txq->tx_dropped++; + return NETDEV_TX_OK; } -- cgit v1.1 From a8fdf2b331b38d61fb5f11f3aec4a4f9fb2dedcb Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Thu, 3 Sep 2009 20:44:38 -0700 Subject: ipv6: Fix tcp_v6_send_response(): it didn't set skb transport header Here is a patch which fixes an issue observed when using TCP over IPv6 and AH from IPsec. When a connection gets closed the 4-way method and the last ACK from the server gets dropped, the subsequent FINs from the client do not get ACKed because tcp_v6_send_response does not set the transport header pointer. This causes ah6_output to try to allocate a lot of memory, which typically fails, so the ACKs never make it out of the stack. I have reproduced the problem on kernel 2.6.7, but after looking at the latest kernel it seems the problem is still there. Signed-off-by: Cosmin Ratiu Signed-off-by: David S. Miller --- net/ipv6/tcp_ipv6.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 65aecf2..3aae0f2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1003,6 +1003,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len); t1 = (struct tcphdr *) skb_push(buff, tot_len); + skb_reset_transport_header(skb); /* Swap the send and the receive. */ memset(t1, 0, sizeof(*t1)); -- cgit v1.1 From 425e0f685230986511b1fdf80340e2f28b214c5d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 16 Jun 2009 14:47:30 +0800 Subject: sctp: avoid overwrite the return value of sctp_process_asconf_ack() The return value of sctp_process_asconf_ack() may be overwritten while process parameters with no error. This patch fixed the problem. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_make_chunk.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 61cc607..b7acc9c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3104,7 +3104,7 @@ done: } /* Process a asconf parameter that is successfully acked. */ -static int sctp_asconf_param_success(struct sctp_association *asoc, +static void sctp_asconf_param_success(struct sctp_association *asoc, sctp_addip_param_t *asconf_param) { struct sctp_af *af; @@ -3113,7 +3113,6 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, union sctp_addr_param *addr_param; struct sctp_transport *transport; struct sctp_sockaddr_entry *saddr; - int retval = 0; addr_param = (union sctp_addr_param *) ((void *)asconf_param + sizeof(sctp_addip_param_t)); @@ -3136,7 +3135,7 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, break; case SCTP_PARAM_DEL_IP: local_bh_disable(); - retval = sctp_del_bind_addr(bp, &addr); + sctp_del_bind_addr(bp, &addr); local_bh_enable(); list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { @@ -3148,8 +3147,6 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, default: break; } - - return retval; } /* Get the corresponding ASCONF response error code from the ASCONF_ACK chunk @@ -3266,7 +3263,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, switch (err_code) { case SCTP_ERROR_NO_ERROR: - retval = sctp_asconf_param_success(asoc, asconf_param); + sctp_asconf_param_success(asoc, asconf_param); break; case SCTP_ERROR_RSRC_LOW: -- cgit v1.1 From 44e65c1ef1e771b32c82546ebfba910137aa8871 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 16 Jun 2009 14:48:24 +0800 Subject: sctp: check the unrecognized ASCONF parameter before access it This patch fix to check the unrecognized ASCONF parameter before access it. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_make_chunk.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index b7acc9c..3d867ce 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2861,6 +2861,11 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, addr_param = (union sctp_addr_param *) ((void *)asconf_param + sizeof(sctp_addip_param_t)); + if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && + asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && + asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) + return SCTP_ERROR_UNKNOWN_PARAM; + switch (addr_param->v4.param_hdr.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) @@ -2958,9 +2963,6 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, sctp_assoc_set_primary(asoc, peer); break; - default: - return SCTP_ERROR_UNKNOWN_PARAM; - break; } return SCTP_ERROR_NO_ERROR; -- cgit v1.1 From 3cd9749c0b758223a71e059fa44c2234547d9ee0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 16 Jun 2009 10:07:23 +0800 Subject: sctp: update the route for non-active transports after addresses are added Update the route and saddr entries for the non-active transports as some of the added addresses can be used as better source addresses, or may be there is a better route. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_make_chunk.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 3d867ce..9d881a6 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3134,6 +3134,14 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, saddr->state = SCTP_ADDR_SRC; } local_bh_enable(); + list_for_each_entry(transport, &asoc->peer.transport_addr_list, + transports) { + if (transport->state == SCTP_ACTIVE) + continue; + dst_release(transport->dst); + sctp_transport_route(transport, NULL, + sctp_sk(asoc->base.sk)); + } break; case SCTP_PARAM_DEL_IP: local_bh_disable(); -- cgit v1.1 From 40187886bc31aee9c5c6f08f46cde4ab618e9736 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 23 Jun 2009 11:28:05 -0400 Subject: sctp: release cached route when the transport goes down. When the sctp transport is marked down, we can release the cached route and force a new lookup when attempting to use this transport for anything. This way, if a better route or source address is available, we'll try to use it. Signed-off-by: Vlad Yasevich --- net/sctp/associola.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 525864b..215b569 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -810,11 +810,16 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, break; case SCTP_TRANSPORT_DOWN: - /* if the transort was never confirmed, do not transition it - * to inactive state. + /* If the transport was never confirmed, do not transition it + * to inactive state. Also, release the cached route since + * there may be a better route next time. */ if (transport->state != SCTP_UNCONFIRMED) transport->state = SCTP_INACTIVE; + else { + dst_release(transport->dst); + transport->dst = NULL; + } spc_state = SCTP_ADDR_UNREACHABLE; break; -- cgit v1.1 From af87b823ca2b05257192e8d48dc686db6173d7b2 Mon Sep 17 00:00:00 2001 From: Doug Graham Date: Wed, 29 Jul 2009 12:05:57 -0400 Subject: sctp: Fix piggybacked ACKs This patch corrects the conditions under which a SACK will be piggybacked on a DATA packet. The previous condition was incorrect due to a misinterpretation of RFC 4960 and/or RFC 2960. Specifically, the following paragraph from section 6.2 had not been implemented correctly: Before an endpoint transmits a DATA chunk, if any received DATA chunks have not been acknowledged (e.g., due to delayed ack), the sender should create a SACK and bundle it with the outbound DATA chunk, as long as the size of the final SCTP packet does not exceed the current MTU. See Section 6.2. When about to send a DATA chunk, the code now checks to see if the SACK timer is running. If it is, we know we have a SACK to send to the peer, so we append the SACK (assuming available space in the packet) and turn off the timer. For a simple request-response scenario, this will result in the SACK being bundled with the response, meaning the the SACK is received quickly by the client, and also meaning that no separate SACK packet needs to be sent by the server to acknowledge the request. Prior to this patch, a separate SACK packet would have been sent by the server SCTP only after its delayed-ACK timer had expired (usually 200ms). This is wasteful of bandwidth, and can also have a major negative impact on performance due the interaction of delayed ACKs with the Nagle algorithm. Signed-off-by: Doug Graham Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index b94c211..94c110d 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -234,18 +234,19 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, if (sctp_chunk_is_data(chunk) && !pkt->has_sack && !pkt->has_cookie_echo) { struct sctp_association *asoc; + struct timer_list *timer; asoc = pkt->transport->asoc; + timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; - if (asoc->a_rwnd > asoc->rwnd) { + /* If the SACK timer is running, we have a pending SACK */ + if (timer_pending(timer)) { struct sctp_chunk *sack; asoc->a_rwnd = asoc->rwnd; sack = sctp_make_sack(asoc); if (sack) { - struct timer_list *timer; retval = sctp_packet_append_chunk(pkt, sack); asoc->peer.sack_needed = 0; - timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; - if (timer_pending(timer) && del_timer(timer)) + if (del_timer(timer)) sctp_association_put(asoc); } } -- cgit v1.1 From bec9640bb0d451813b1bb1f2cc13a5bfb17c3e48 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Thu, 30 Jul 2009 18:08:28 -0400 Subject: sctp: Disallow new connection on a closing socket If a socket has a lot of association that are in the process of of being closed/aborted, it is possible for a remote to establish new associations during the time period that the old ones are shutting down. If this was a result of a close() call, there will be no socket and will cause a memory leak. We'll prevent this by setting the socket state to CLOSING and disallow new associations when in this state. Signed-off-by: Vlad Yasevich --- net/sctp/sm_statefuns.c | 9 +++++++++ net/sctp/socket.c | 1 + 2 files changed, 10 insertions(+) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7288192..50225dd 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -334,6 +334,15 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* If the INIT is coming toward a closing socket, we'll send back + * and ABORT. Essentially, this catches the race of INIT being + * backloged to the socket at the same time as the user isses close(). + * Since the socket and all its associations are going away, we + * can treat this OOTB + */ + if (sctp_sstate(ep->base.sk, CLOSING)) + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + /* Verify the INIT chunk before processing it. */ err_chunk = NULL; if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 971890d..a7e544e 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1361,6 +1361,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) sctp_lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; + sk->sk_state = SCTP_SS_CLOSING; ep = sctp_sk(sk)->ep; -- cgit v1.1 From 3e62abf92f34d75fe22352d8d102e3cd2755804d Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:56 -0400 Subject: sctp: Fix data segmentation with small frag_size Since an application may specify the maximum SCTP fragment size that all data should be fragmented to, we need to fix how we do segmentation. Right now, if a user specifies a small fragment size, the segment size can go negative in the presence of AUTH or COOKIE_ECHO bundling. What we need to do is track the largest possbile DATA chunk that can fit into the mtu. Then if the fragment size specified is bigger then this maximum length, we'll shrink it down. Otherwise, we just use the smaller segment size without changing it further. Signed-off-by: Vlad Yasevich --- net/sctp/chunk.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 1748ef9..9292294 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -158,6 +158,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, { int max, whole, i, offset, over, err; int len, first_len; + int max_data; struct sctp_chunk *chunk; struct sctp_datamsg *msg; struct list_head *pos, *temp; @@ -179,8 +180,14 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, __func__, msg, msg->expires_at, jiffies); } - max = asoc->frag_point; + /* This is the biggest possible DATA chunk that can fit into + * the packet + */ + max_data = asoc->pathmtu - + sctp_sk(asoc->base.sk)->pf->af->net_header_len - + sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk); + max = asoc->frag_point; /* If the the peer requested that we authenticate DATA chunks * we need to accound for bundling of the AUTH chunks along with * DATA. @@ -189,23 +196,30 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc); if (hmac_desc) - max -= WORD_ROUND(sizeof(sctp_auth_chunk_t) + + max_data -= WORD_ROUND(sizeof(sctp_auth_chunk_t) + hmac_desc->hmac_len); } + /* Now, check if we need to reduce our max */ + if (max > max_data) + max = max_data; + whole = 0; first_len = max; /* Encourage Cookie-ECHO bundling. */ if (asoc->state < SCTP_STATE_COOKIE_ECHOED) { - whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN); + max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN; - /* Account for the DATA to be bundled with the COOKIE-ECHO. */ - if (whole) { - first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN; - msg_len -= first_len; - whole = 1; - } + /* This is the biggesr first_len we can have */ + if (first_len > max_data) + first_len = max_data; + } + + /* Account for a different sized first fragment */ + if (msg_len >= first_len) { + msg_len -= first_len; + whole = 1; } /* How many full sized? How many bytes leftover? */ -- cgit v1.1 From e83963b769a2c38b436f5dcf82309f5cbc2f6012 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 7 Aug 2009 10:43:07 -0400 Subject: sctp: Generate SACKs when actually sending outbound DATA We are now trying to bundle SACKs when we have outbound DATA to send. However, there are situations where this outbound DATA will not be sent (due to congestion or available window). In such cases it's ok to wait for the timer to expire. This patch refactors the sending code so that betfore attempting to bundle the SACK we check to see if the DATA will actually be transmitted. Based on eirlier works for Doug Graham and Wei Youngjun . Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 142 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 57 deletions(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index 94c110d..e25e2e2 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -61,8 +61,13 @@ #include /* Forward declarations for private helpers. */ -static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, +static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, struct sctp_chunk *chunk); +static void sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); +static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len); /* Config a packet. * This appears to be a followup set of initializations. @@ -262,13 +267,20 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, { sctp_xmit_t retval = SCTP_XMIT_OK; __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); - size_t psize; - size_t pmtu; - int too_big; SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__, packet, chunk); + /* Data chunks are special. Before seeing what else we can + * bundle into this packet, check to see if we are allowed to + * send this DATA. + */ + if (sctp_chunk_is_data(chunk)) { + retval = sctp_packet_can_append_data(packet, chunk); + if (retval != SCTP_XMIT_OK) + goto finish; + } + /* Try to bundle AUTH chunk */ retval = sctp_packet_bundle_auth(packet, chunk); if (retval != SCTP_XMIT_OK) @@ -279,51 +291,16 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, if (retval != SCTP_XMIT_OK) goto finish; - psize = packet->size; - pmtu = ((packet->transport->asoc) ? - (packet->transport->asoc->pathmtu) : - (packet->transport->pathmtu)); - - too_big = (psize + chunk_len > pmtu); - - /* Decide if we need to fragment or resubmit later. */ - if (too_big) { - /* It's OK to fragmet at IP level if any one of the following - * is true: - * 1. The packet is empty (meaning this chunk is greater - * the MTU) - * 2. The chunk we are adding is a control chunk - * 3. The packet doesn't have any data in it yet and data - * requires authentication. - */ - if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) || - (!packet->has_data && chunk->auth)) { - /* We no longer do re-fragmentation. - * Just fragment at the IP layer, if we - * actually hit this condition - */ - packet->ipfragok = 1; - goto append; - - } else { - retval = SCTP_XMIT_PMTU_FULL; - goto finish; - } - } - -append: - /* We believe that this chunk is OK to add to the packet (as - * long as we have the cwnd for it). - */ + /* Check to see if this chunk will fit into the packet */ + retval = sctp_packet_will_fit(packet, chunk, chunk_len); + if (retval != SCTP_XMIT_OK) + goto finish; - /* DATA is a special case since we must examine both rwnd and cwnd - * before we send DATA. - */ + /* We believe that this chunk is OK to add to the packet */ switch (chunk->chunk_hdr->type) { case SCTP_CID_DATA: - retval = sctp_packet_append_data(packet, chunk); - if (SCTP_XMIT_OK != retval) - goto finish; + /* Account for the data being in the packet */ + sctp_packet_append_data(packet, chunk); /* Disallow SACK bundling after DATA. */ packet->has_sack = 1; /* Disallow AUTH bundling after DATA */ @@ -633,16 +610,15 @@ nomem: * 2nd Level Abstractions ********************************************************************/ -/* This private function handles the specifics of appending DATA chunks. */ -static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, +/* This private function check to see if a chunk can be added */ +static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; - size_t datasize, rwnd, inflight; + size_t datasize, rwnd, inflight, flight_size; struct sctp_transport *transport = packet->transport; __u32 max_burst_bytes; struct sctp_association *asoc = transport->asoc; - struct sctp_sock *sp = sctp_sk(asoc->base.sk); struct sctp_outq *q = &asoc->outqueue; /* RFC 2960 6.1 Transmission of DATA Chunks @@ -659,7 +635,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, */ rwnd = asoc->peer.rwnd; - inflight = asoc->outqueue.outstanding_bytes; + inflight = q->outstanding_bytes; + flight_size = transport->flight_size; datasize = sctp_data_size(chunk); @@ -682,8 +659,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * cwnd = flightsize + Max.Burst * MTU */ max_burst_bytes = asoc->max_burst * asoc->pathmtu; - if ((transport->flight_size + max_burst_bytes) < transport->cwnd) { - transport->cwnd = transport->flight_size + max_burst_bytes; + if ((flight_size + max_burst_bytes) < transport->cwnd) { + transport->cwnd = flight_size + max_burst_bytes; SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: " "transport: %p, cwnd: %d, " "ssthresh: %d, flight_size: %d, " @@ -708,7 +685,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * ignore the value of cwnd and SHOULD NOT delay retransmission. */ if (chunk->fast_retransmit != SCTP_NEED_FRTX) - if (transport->flight_size >= transport->cwnd) { + if (flight_size >= transport->cwnd) { retval = SCTP_XMIT_RWND_FULL; goto finish; } @@ -718,8 +695,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, * if any previously transmitted data on the connection remains * unacknowledged. */ - if (!sp->nodelay && sctp_packet_empty(packet) && - q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) { + if (!sctp_sk(asoc->base.sk)->nodelay && sctp_packet_empty(packet) && + inflight && sctp_state(asoc, ESTABLISHED)) { unsigned len = datasize + q->out_qlen; /* Check whether this chunk and all the rest of pending @@ -732,6 +709,19 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, } } +finish: + return retval; +} + +/* This private function does management things when adding DATA chunk */ +static void sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) +{ + struct sctp_transport *transport = packet->transport; + size_t datasize = sctp_data_size(chunk); + struct sctp_association *asoc = transport->asoc; + u32 rwnd = asoc->peer.rwnd; + /* Keep track of how many bytes are in flight over this transport. */ transport->flight_size += datasize; @@ -754,7 +744,45 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, /* Has been accepted for transmission. */ if (!asoc->peer.prsctp_capable) chunk->msg->can_abandon = 0; +} + +static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len) +{ + size_t psize; + size_t pmtu; + int too_big; + sctp_xmit_t retval = SCTP_XMIT_OK; + + psize = packet->size; + pmtu = ((packet->transport->asoc) ? + (packet->transport->asoc->pathmtu) : + (packet->transport->pathmtu)); + + too_big = (psize + chunk_len > pmtu); + + /* Decide if we need to fragment or resubmit later. */ + if (too_big) { + /* It's OK to fragmet at IP level if any one of the following + * is true: + * 1. The packet is empty (meaning this chunk is greater + * the MTU) + * 2. The chunk we are adding is a control chunk + * 3. The packet doesn't have any data in it yet and data + * requires authentication. + */ + if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) || + (!packet->has_data && chunk->auth)) { + /* We no longer do re-fragmentation. + * Just fragment at the IP layer, if we + * actually hit this condition + */ + packet->ipfragok = 1; + } else { + retval = SCTP_XMIT_PMTU_FULL; + } + } -finish: return retval; } -- cgit v1.1 From 5d7ff261ef497c62f54c39effc259910a28b313d Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 7 Aug 2009 13:23:28 -0400 Subject: sctp: Try to encourage SACK bundling with DATA. If the association has a SACK timer pending and now DATA queued to be send, we'll try to bundle the SACK with the next application send. As such, try encourage bundling by accounting for SACK in the size of the first chunk fragment. Signed-off-by: Vlad Yasevich --- net/sctp/chunk.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 9292294..7acaf15 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -207,14 +207,25 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, whole = 0; first_len = max; + /* Check to see if we have a pending SACK and try to let it be bundled + * with this message. Do this if we don't have any data queued already. + * To check that, look at out_qlen and retransmit list. + * NOTE: we will not reduce to account for SACK, if the message would + * not have been fragmented. + */ + if (timer_pending(&asoc->timers[SCTP_EVENT_TIMEOUT_SACK]) && + asoc->outqueue.out_qlen == 0 && + list_empty(&asoc->outqueue.retransmit) && + msg_len > max) + max_data -= WORD_ROUND(sizeof(sctp_sack_chunk_t)); + /* Encourage Cookie-ECHO bundling. */ - if (asoc->state < SCTP_STATE_COOKIE_ECHOED) { + if (asoc->state < SCTP_STATE_COOKIE_ECHOED) max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN; - /* This is the biggesr first_len we can have */ - if (first_len > max_data) - first_len = max_data; - } + /* Now that we adjusted completely, reset first_len */ + if (first_len > max_data) + first_len = max_data; /* Account for a different sized first fragment */ if (msg_len >= first_len) { -- cgit v1.1 From 9c5c62be2f794c7cee533d856f9f34c3cf21ff1b Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Mon, 10 Aug 2009 13:51:03 -0400 Subject: sctp: Send user messages to the lower layer as one Currenlty, sctp breaks up user messages into fragments and sends each fragment to the lower layer by itself. This means that for each fragment we go all the way down the stack and back up. This also discourages bundling of multiple fragments when they can fit into a sigle packet (ex: due to user setting a low fragmentation threashold). We introduce a new command SCTP_CMD_SND_MSG and hand the whole message down state machine. The state machine and the side-effect parser will cork the queue, add all chunks from the message to the queue, and then un-cork the queue thus causing the chunks to get transmitted. Signed-off-by: Vlad Yasevich --- net/sctp/chunk.c | 13 +++++++++++++ net/sctp/sm_sideeffect.c | 29 ++++++++++++++++++++++++++++- net/sctp/sm_statefuns.c | 4 ++-- net/sctp/socket.c | 26 ++++++++++++++------------ 4 files changed, 57 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 7acaf15..645577d 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -73,6 +73,19 @@ SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp) return msg; } +void sctp_datamsg_free(struct sctp_datamsg *msg) +{ + struct sctp_chunk *chunk; + + /* This doesn't have to be a _safe vairant because + * sctp_chunk_free() only drops the refs. + */ + list_for_each_entry(chunk, &msg->chunks, frag_list) + sctp_chunk_free(chunk); + + sctp_datamsg_put(msg); +} + /* Final destructruction of datamsg memory. */ static void sctp_datamsg_destroy(struct sctp_datamsg *msg) { diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 86426aa..238adf7 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -931,6 +931,27 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc, } +/* Send the whole message, chunk by chunk, to the outqueue. + * This way the whole message is queued up and bundling if + * encouraged for small fragments. + */ +static int sctp_cmd_send_msg(struct sctp_association *asoc, + struct sctp_datamsg *msg) +{ + struct sctp_chunk *chunk; + int error = 0; + + list_for_each_entry(chunk, &msg->chunks, frag_list) { + error = sctp_outq_tail(&asoc->outqueue, chunk); + if (error) + break; + } + + return error; +} + + + /* These three macros allow us to pull the debugging code out of the * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. @@ -1575,7 +1596,13 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_UPDATE_INITTAG: asoc->peer.i.init_tag = cmd->obj.u32; break; - + case SCTP_CMD_SEND_MSG: + if (!asoc->outqueue.cork) { + sctp_outq_cork(&asoc->outqueue); + local_cork = 1; + } + error = sctp_cmd_send_msg(asoc, cmd->obj.msg); + break; default: printk(KERN_WARNING "Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 50225dd..9109269 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4555,9 +4555,9 @@ sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { - struct sctp_chunk *chunk = arg; + struct sctp_datamsg *msg = arg; - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk)); + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_MSG, SCTP_DATAMSG(msg)); return SCTP_DISPOSITION_CONSUME; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a7e544e..95a5623 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1814,20 +1814,22 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, sctp_set_owner_w(chunk); chunk->transport = chunk_tp; - - /* Send it to the lower layers. Note: all chunks - * must either fail or succeed. The lower layer - * works that way today. Keep it that way or this - * breaks. - */ - err = sctp_primitive_SEND(asoc, chunk); - /* Did the lower layer accept the chunk? */ - if (err) - sctp_chunk_free(chunk); - SCTP_DEBUG_PRINTK("We sent primitively.\n"); } - sctp_datamsg_put(datamsg); + /* Send it to the lower layers. Note: all chunks + * must either fail or succeed. The lower layer + * works that way today. Keep it that way or this + * breaks. + */ + err = sctp_primitive_SEND(asoc, datamsg); + /* Did the lower layer accept the chunk? */ + if (err) + sctp_datamsg_free(datamsg); + else + sctp_datamsg_put(datamsg); + + SCTP_DEBUG_PRINTK("We sent primitively.\n"); + if (err) goto out_free; else -- cgit v1.1 From a2f36eec5647548fa94fb68e2843b00fb9c0d46b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Aug 2009 11:24:00 +0800 Subject: sctp: drop SHUTDOWN chunk if the TSN is less than the CTSN If Cumulative TSN Ack field of SHUTDOWN chunk is less than the Cumulative TSN Ack Point then drop the SHUTDOWN chunk. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_statefuns.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9109269..73bdeb2 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2570,6 +2570,12 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, chunk->subh.shutdown_hdr = sdh; ctsn = ntohl(sdh->cum_tsn_ack); + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } + /* If Cumulative TSN Ack beyond the max tsn currently * send, terminating the association and respond to the * sender with an ABORT. @@ -2633,6 +2639,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; sctp_shutdownhdr_t *sdh; + __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -2644,12 +2651,19 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep, commands); sdh = (sctp_shutdownhdr_t *)chunk->skb->data; + ctsn = ntohl(sdh->cum_tsn_ack); + + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } /* If Cumulative TSN Ack beyond the max tsn currently * send, terminating the association and respond to the * sender with an ABORT. */ - if (!TSN_lt(ntohl(sdh->cum_tsn_ack), asoc->next_tsn)) + if (!TSN_lt(ctsn, asoc->next_tsn)) return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands); /* verify, by checking the Cumulative TSN Ack field of the -- cgit v1.1 From dadb50cc1ada2906594df83d991f0bc388039bb6 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Aug 2009 11:27:37 +0800 Subject: sctp: fix check the chunk length of received HEARTBEAT-ACK chunk The receiver of the HEARTBEAT should respond with a HEARTBEAT ACK that contains the Heartbeat Information field copied from the received HEARTBEAT chunk. So the received HEARTBEAT-ACK chunk must have a length of: sizeof(sctp_chunkhdr_t) + sizeof(sctp_sender_hb_info_t) A badly formatted HB-ACK chunk, it is possible that we may access invalid memory. We should really make sure that the chunk format is what we expect, before attempting to touch the data. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_statefuns.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 73bdeb2..7fb08a6 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1115,7 +1115,8 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT-ACK chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t) + + sizeof(sctp_sender_hb_info_t))) return sctp_sf_violation_chunklen(ep, asoc, type, arg, commands); -- cgit v1.1 From d71a09ed555e52299b5d3ad8945bdf84f65423a6 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sun, 23 Aug 2009 23:11:36 +0400 Subject: sctp: use proc_create() create_proc_entry() is deprecated (not formally, though). Signed-off-by: Alexey Dobriyan Signed-off-by: Vlad Yasevich --- net/sctp/proc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/sctp/proc.c b/net/sctp/proc.c index f268910..d093cbf 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -512,10 +512,8 @@ int __init sctp_remaddr_proc_init(void) { struct proc_dir_entry *p; - p = create_proc_entry("remaddr", S_IRUGO, proc_net_sctp); + p = proc_create("remaddr", S_IRUGO, proc_net_sctp, &sctp_remaddr_seq_fops); if (!p) return -ENOMEM; - p->proc_fops = &sctp_remaddr_seq_fops; - return 0; } -- cgit v1.1 From b9f8478682445c2a3e0b87718a0563ef543ad94e Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 26 Aug 2009 09:36:25 -0400 Subject: sctp: Fix error count increments that were results of HEARTBEATS SCTP RFC 4960 states that unacknowledged HEARTBEATS count as errors agains a given transport or endpoint. As such, we should increment the error counts for only for unacknowledged HB, otherwise we detect failure too soon. This goes for both the overall error count and the path error count. Now, there is a difference in how the detection is done between the two. The path error detection is done after the increment, so to detect it properly, we actually need to exceed the path threshold. The overall error detection is done _BEFORE_ the increment. Thus to detect the failure, it's enough for the error count to match the threshold. This is why all the state functions use '>=' to detect failure, while path detection uses '>'. Thanks goes to Chunbo Luo who first proposed patches to fix this issue and made me re-read the spec and the code to figure out how this cruft really works. Signed-off-by: Vlad Yasevich --- net/sctp/sm_sideeffect.c | 20 ++++++++++++++++---- net/sctp/sm_statefuns.c | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 238adf7..694f749 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -440,14 +440,26 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, /* The check for association's overall error counter exceeding the * threshold is done in the state function. */ - /* When probing UNCONFIRMED addresses, the association overall - * error count is NOT incremented + /* We are here due to a timer expiration. If the timer was + * not a HEARTBEAT, then normal error tracking is done. + * If the timer was a heartbeat, we only increment error counts + * when we already have an outstanding HEARTBEAT that has not + * been acknowledged. + * Additionaly, some tranport states inhibit error increments. */ - if (transport->state != SCTP_UNCONFIRMED) + if (!is_hb) { asoc->overall_error_count++; + if (transport->state != SCTP_INACTIVE) + transport->error_count++; + } else if (transport->hb_sent) { + if (transport->state != SCTP_UNCONFIRMED) + asoc->overall_error_count++; + if (transport->state != SCTP_INACTIVE) + transport->error_count++; + } if (transport->state != SCTP_INACTIVE && - (transport->error_count++ >= transport->pathmaxrxt)) { + (transport->error_count > transport->pathmaxrxt)) { SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p", " transport IP: port:%d failed.\n", asoc, diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7fb08a6..45b8bca 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -971,7 +971,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, { struct sctp_transport *transport = (struct sctp_transport *) arg; - if (asoc->overall_error_count > asoc->max_retrans) { + if (asoc->overall_error_count >= asoc->max_retrans) { sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ETIMEDOUT)); /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ -- cgit v1.1 From 33ce828131ca6655b48bd2070dadd80f816dfe0d Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:58 -0400 Subject: sctp: Clear fast_recovery on the transport when T3 timer expires. If T3 timer expires, we are retransmitting data due to timeout any any fast recovery is null and void. We can clear the fast recovery flag. Signed-off-by: Vlad Yasevich --- net/sctp/transport.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/sctp/transport.c b/net/sctp/transport.c index e5dde45..c256e48 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -503,6 +503,9 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport, transport->ssthresh = max(transport->cwnd/2, 4*transport->asoc->pathmtu); transport->cwnd = transport->asoc->pathmtu; + + /* T3-rtx also clears fast recovery on the transport */ + transport->fast_recovery = 0; break; case SCTP_LOWER_CWND_FAST_RTX: -- cgit v1.1 From 4d3c46e6833208428d366630aa708f6876e61fc1 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:59 -0400 Subject: sctp: drop a_rwnd to 0 when receive buffer overflows. SCTP has a problem that when small chunks are used, it is possible to exhaust the receiver buffer without fully closing receive window. This happens due to all overhead that we have account for with small messages. To fix this, when receive buffer is exceeded, we'll drop the window to 0 and save the 'drop' portion. When application starts reading data and freeing up recevie buffer space, we'll wait until we've reached the 'drop' window and then add back this 'drop' one mtu at a time. This worked well in testing and under stress produced rather even recovery. Signed-off-by: Vlad Yasevich --- net/sctp/associola.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 215b569..39c3821 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -202,6 +202,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->a_rwnd = asoc->rwnd; asoc->rwnd_over = 0; + asoc->rwnd_press = 0; /* Use my own max window until I learn something better. */ asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW; @@ -1374,6 +1375,17 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) asoc->rwnd += len; } + /* If we had window pressure, start recovering it + * once our rwnd had reached the accumulated pressure + * threshold. The idea is to recover slowly, but up + * to the initial advertised window. + */ + if (asoc->rwnd_press && asoc->rwnd >= asoc->rwnd_press) { + int change = min(asoc->pathmtu, asoc->rwnd_press); + asoc->rwnd += change; + asoc->rwnd_press -= change; + } + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) " "- %u\n", __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, asoc->a_rwnd); @@ -1406,17 +1418,38 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) /* Decrease asoc's rwnd by len. */ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len) { + int rx_count; + int over = 0; + SCTP_ASSERT(asoc->rwnd, "rwnd zero", return); SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return); + + if (asoc->ep->rcvbuf_policy) + rx_count = atomic_read(&asoc->rmem_alloc); + else + rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc); + + /* If we've reached or overflowed our receive buffer, announce + * a 0 rwnd if rwnd would still be positive. Store the + * the pottential pressure overflow so that the window can be restored + * back to original value. + */ + if (rx_count >= asoc->base.sk->sk_rcvbuf) + over = 1; + if (asoc->rwnd >= len) { asoc->rwnd -= len; + if (over) { + asoc->rwnd_press = asoc->rwnd; + asoc->rwnd = 0; + } } else { asoc->rwnd_over = len - asoc->rwnd; asoc->rwnd = 0; } - SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u, %u)\n", __func__, asoc, len, asoc->rwnd, - asoc->rwnd_over); + asoc->rwnd_over, asoc->rwnd_press); } /* Build the bind address list for the association based on info from the -- cgit v1.1 From d4d6fb5787a6ef6e1dab731d617ebda6be73d561 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:59 -0400 Subject: sctp: Try not to change a_rwnd when faking a SACK from SHUTDOWN. We currently set a_rwnd to 0 when faking a SACK from SHUTDOWN. This results in an hung association if the remote only uses SHUTDOWNs (which it's allowed to do) to acknowlege DATA when closing. The reason for that is that we simply honor the a_rwnd from the sack, but since we faked it to be 0, we enter 0-window probing. The fix is to use the peers old rwnd and add our flight size to it. Signed-off-by: Vlad Yasevich --- net/sctp/sm_sideeffect.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 694f749..8674d49 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1533,7 +1533,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_PROCESS_CTSN: /* Dummy up a SACK for processing. */ sackh.cum_tsn_ack = cmd->obj.be32; - sackh.a_rwnd = 0; + sackh.a_rwnd = asoc->peer.rwnd + + asoc->outqueue.outstanding_bytes; sackh.num_gap_ack_blocks = 0; sackh.num_dup_tsns = 0; sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, @@ -1632,9 +1633,9 @@ out: */ if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) { if (chunk->end_of_packet || chunk->singleton) - sctp_outq_uncork(&asoc->outqueue); + error = sctp_outq_uncork(&asoc->outqueue); } else if (local_cork) - sctp_outq_uncork(&asoc->outqueue); + error = sctp_outq_uncork(&asoc->outqueue); return error; nomem: error = -ENOMEM; -- cgit v1.1 From b29e7907288554db9c987c36facfd0304ee8ff5a Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:59 -0400 Subject: sctp: Nagle delay should be based on path mtu The decision to delay due to Nagle should be based on the path mtu and future packet size. We currently incorrectly base it on 'frag_point' which is the SCTP DATA segment size, and also we do not count DATA chunk header overhead in the computation. This actuall allows situations where a user can set low 'frag_point', and then send small messages without delay. Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index e25e2e2..d0b84f6 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -697,13 +697,14 @@ static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, */ if (!sctp_sk(asoc->base.sk)->nodelay && sctp_packet_empty(packet) && inflight && sctp_state(asoc, ESTABLISHED)) { - unsigned len = datasize + q->out_qlen; + unsigned max = transport->pathmtu - packet->overhead; + unsigned len = chunk->skb->len + q->out_qlen; /* Check whether this chunk and all the rest of pending * data will fit or delay in hopes of bundling a full * sized packet. */ - if (len < asoc->frag_point) { + if (len < max) { retval = SCTP_XMIT_NAGLE_DELAY; goto finish; } -- cgit v1.1 From cb95ea32a457871f72752164de8d94fa20f4703c Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:20:59 -0400 Subject: sctp: Don't do NAGLE delay on large writes that were fragmented small SCTP will delay the last part of a large write due to NAGLE, if that part is smaller then MTU. Since we are doing large writes, we might as well send the last portion now instead of waiting untill the next large write happens. The small portion will be sent as is regardless, so it's better to not delay it. This is a result of much discussions with Wei Yongjun and Doug Graham . Many thanks go out to them. Signed-off-by: Vlad Yasevich --- net/sctp/chunk.c | 2 ++ net/sctp/output.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 645577d..acf7c4d 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -59,6 +59,7 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg) msg->can_abandon = 0; msg->expires_at = 0; INIT_LIST_HEAD(&msg->chunks); + msg->msg_size = 0; } /* Allocate and initialize datamsg. */ @@ -155,6 +156,7 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu { sctp_datamsg_hold(msg); chunk->msg = msg; + msg->msg_size += chunk->skb->len; } diff --git a/net/sctp/output.c b/net/sctp/output.c index d0b84f6..b801bc9 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -703,8 +703,10 @@ static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, /* Check whether this chunk and all the rest of pending * data will fit or delay in hopes of bundling a full * sized packet. + * Don't delay large message writes that may have been + * fragmeneted into small peices. */ - if (len < max) { + if ((len < max) && (chunk->msg->msg_size < max)) { retval = SCTP_XMIT_NAGLE_DELAY; goto finish; } -- cgit v1.1 From f68b2e05f326971cd76c65aa91a1a41771dd7485 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:21:00 -0400 Subject: sctp: Fix SCTP_MAXSEG socket option to comply to spec. We had a bug that we never stored the user-defined value for MAXSEG when setting the value on an association. Thus future PMTU events ended up re-writing the frag point and increasing it past user limit. Additionally, when setting the option on the socket/endpoint, we effect all current associations, which is against spec. Now, we store the user 'maxseg' value along with the computed 'frag_point'. We inherit 'maxseg' from the socket at association creation and use it as an upper limit for 'frag_point' when its set. Signed-off-by: Vlad Yasevich --- net/sctp/associola.c | 6 +++--- net/sctp/socket.c | 11 +++-------- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 39c3821..1f05b94 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -112,6 +112,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000) * 1000; asoc->frag_point = 0; + asoc->user_frag = sp->user_frag; /* Set the association max_retrans and RTO values from the * socket values. @@ -674,7 +675,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, "%d\n", asoc, asoc->pathmtu); peer->pmtu_pending = 0; - asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); + asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); /* The asoc->peer.port might not be meaningful yet, but * initialize the packet structure anyway. @@ -1330,9 +1331,8 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc) } if (pmtu) { - struct sctp_sock *sp = sctp_sk(asoc->base.sk); asoc->pathmtu = pmtu; - asoc->frag_point = sctp_frag_point(sp, pmtu); + asoc->frag_point = sctp_frag_point(asoc, pmtu); } SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 95a5623..89af37a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2243,7 +2243,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, sctp_assoc_sync_pmtu(asoc); } else if (asoc) { asoc->pathmtu = params->spp_pathmtu; - sctp_frag_point(sp, params->spp_pathmtu); + sctp_frag_point(asoc, params->spp_pathmtu); } else { sp->pathmtu = params->spp_pathmtu; } @@ -2880,15 +2880,10 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl val -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk); } - - asoc->frag_point = val; + asoc->user_frag = val; + asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); } else { sp->user_frag = val; - - /* Update the frag_point of the existing associations. */ - list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { - asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); - } } return 0; -- cgit v1.1 From 31b02e1549406efa346534acad956a42bc3f28c4 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:21:00 -0400 Subject: sctp: Failover transmitted list on transport delete Add-IP feature allows users to delete an active transport. If that transport has chunks in flight, those chunks need to be moved to another transport or association may get into unrecoverable state. Reported-by: Rafael Laufer Signed-off-by: Vlad Yasevich --- net/sctp/associola.c | 27 +++++++++++++++++++++++++++ net/sctp/outqueue.c | 47 ++++++++++++++++++++++++++++++++++------------- net/sctp/sm_statefuns.c | 6 ++++++ 3 files changed, 67 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 1f05b94..caba989 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -584,6 +584,33 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, asoc->addip_last_asconf->transport == peer) asoc->addip_last_asconf->transport = NULL; + /* If we have something on the transmitted list, we have to + * save it off. The best place is the active path. + */ + if (!list_empty(&peer->transmitted)) { + struct sctp_transport *active = asoc->peer.active_path; + struct sctp_chunk *ch; + + /* Reset the transport of each chunk on this list */ + list_for_each_entry(ch, &peer->transmitted, + transmitted_list) { + ch->transport = NULL; + ch->rtt_in_progress = 0; + } + + list_splice_tail_init(&peer->transmitted, + &active->transmitted); + + /* Start a T3 timer here in case it wasn't running so + * that these migrated packets have a chance to get + * retrnasmitted. + */ + if (!timer_pending(&active->T3_rtx_timer)) + if (!mod_timer(&active->T3_rtx_timer, + jiffies + active->rto)) + sctp_transport_hold(active); + } + asoc->peer.transport_count--; sctp_transport_free(peer); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index d765fc5..c9f20e2 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -406,8 +406,9 @@ void sctp_retransmit_mark(struct sctp_outq *q, * not be retransmitted */ if (!chunk->tsn_gap_acked) { - chunk->transport->flight_size -= - sctp_data_size(chunk); + if (chunk->transport) + chunk->transport->flight_size -= + sctp_data_size(chunk); q->outstanding_bytes -= sctp_data_size(chunk); q->asoc->peer.rwnd += (sctp_data_size(chunk) + sizeof(struct sk_buff)); @@ -443,7 +444,8 @@ void sctp_retransmit_mark(struct sctp_outq *q, q->asoc->peer.rwnd += (sctp_data_size(chunk) + sizeof(struct sk_buff)); q->outstanding_bytes -= sctp_data_size(chunk); - transport->flight_size -= sctp_data_size(chunk); + if (chunk->transport) + transport->flight_size -= sctp_data_size(chunk); /* sctpimpguide-05 Section 2.8.2 * M5) If a T3-rtx timer expires, the @@ -1310,6 +1312,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, __u32 rtt; __u8 restart_timer = 0; int bytes_acked = 0; + int migrate_bytes = 0; /* These state variables are for coherent debug output. --xguo */ @@ -1343,8 +1346,9 @@ static void sctp_check_transmitted(struct sctp_outq *q, * considering it as 'outstanding'. */ if (!tchunk->tsn_gap_acked) { - tchunk->transport->flight_size -= - sctp_data_size(tchunk); + if (tchunk->transport) + tchunk->transport->flight_size -= + sctp_data_size(tchunk); q->outstanding_bytes -= sctp_data_size(tchunk); } continue; @@ -1378,6 +1382,20 @@ static void sctp_check_transmitted(struct sctp_outq *q, rtt); } } + + /* If the chunk hasn't been marked as ACKED, + * mark it and account bytes_acked if the + * chunk had a valid transport (it will not + * have a transport if ASCONF had deleted it + * while DATA was outstanding). + */ + if (!tchunk->tsn_gap_acked) { + tchunk->tsn_gap_acked = 1; + bytes_acked += sctp_data_size(tchunk); + if (!tchunk->transport) + migrate_bytes += sctp_data_size(tchunk); + } + if (TSN_lte(tsn, sack_ctsn)) { /* RFC 2960 6.3.2 Retransmission Timer Rules * @@ -1391,8 +1409,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, restart_timer = 1; if (!tchunk->tsn_gap_acked) { - tchunk->tsn_gap_acked = 1; - bytes_acked += sctp_data_size(tchunk); /* * SFR-CACC algorithm: * 2) If the SACK contains gap acks @@ -1432,10 +1448,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, * older than that newly acknowledged DATA * chunk, are qualified as 'Stray DATA chunks'. */ - if (!tchunk->tsn_gap_acked) { - tchunk->tsn_gap_acked = 1; - bytes_acked += sctp_data_size(tchunk); - } list_add_tail(lchunk, &tlist); } @@ -1491,7 +1503,8 @@ static void sctp_check_transmitted(struct sctp_outq *q, tsn); tchunk->tsn_gap_acked = 0; - bytes_acked -= sctp_data_size(tchunk); + if (tchunk->transport) + bytes_acked -= sctp_data_size(tchunk); /* RFC 2960 6.3.2 Retransmission Timer Rules * @@ -1561,6 +1574,14 @@ static void sctp_check_transmitted(struct sctp_outq *q, #endif /* SCTP_DEBUG */ if (transport) { if (bytes_acked) { + /* We may have counted DATA that was migrated + * to this transport due to DEL-IP operation. + * Subtract those bytes, since the were never + * send on this transport and shouldn't be + * credited to this transport. + */ + bytes_acked -= migrate_bytes; + /* 8.2. When an outstanding TSN is acknowledged, * the endpoint shall clear the error counter of * the destination transport address to which the @@ -1589,7 +1610,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, transport->flight_size -= bytes_acked; if (transport->flight_size == 0) transport->partial_bytes_acked = 0; - q->outstanding_bytes -= bytes_acked; + q->outstanding_bytes -= bytes_acked + migrate_bytes; } else { /* RFC 2960 6.1, sctpimpguide-06 2.15.2 * When a sender is doing zero window probing, it diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 45b8bca..a7f18a3 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3543,6 +3543,12 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, asconf_ack = sctp_assoc_lookup_asconf_ack(asoc, hdr->serial); if (!asconf_ack) return SCTP_DISPOSITION_DISCARD; + + /* Reset the transport so that we select the correct one + * this time around. This is to make sure that we don't + * accidentally use a stale transport that's been removed. + */ + asconf_ack->transport = NULL; } else { /* ADDIP 5.2 E5) Otherwise, the ASCONF Chunk is discarded since * it must be either a stale packet or from an attacker. -- cgit v1.1 From d521c08f4c16d27f193718da778503a6472501da Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 2 Sep 2009 13:05:33 +0800 Subject: sctp: fix to reset packet information after packet transmit The packet information does not reset after packet transmit, this may cause some problems such as following DATA chunk be sent without AUTH chunk, even if the authentication of DATA chunk has been requested by the peer. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index b801bc9..1f93361 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -136,6 +136,17 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, return packet; } +static void sctp_packet_reset(struct sctp_packet *packet) +{ + packet->size = packet->overhead; + packet->has_cookie_echo = 0; + packet->has_sack = 0; + packet->has_data = 0; + packet->has_auth = 0; + packet->ipfragok = 0; + packet->auth = NULL; +} + /* Free a packet. */ void sctp_packet_free(struct sctp_packet *packet) { @@ -576,7 +587,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) (*tp->af_specific->sctp_xmit)(nskb, tp); out: - packet->size = packet->overhead; + sctp_packet_reset(packet); return err; no_route: kfree_skb(nskb); -- cgit v1.1 From 4007cc88ceec8892b74792f0a10983b140beae72 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:21:00 -0400 Subject: sctp: Correctly track if AUTH has been bundled. We currently track if AUTH has been bundled using the 'auth' pointer to the chunk. However, AUTH is disallowed after DATA is already in the packet, so we need to instead use the 'has_auth' field. Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index 1f93361..e47398c 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -220,7 +220,7 @@ static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt, /* See if this is an auth chunk we are bundling or if * auth is already bundled. */ - if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->auth) + if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->has_auth) return retval; /* if the peer did not request this chunk to be authenticated, -- cgit v1.1 From 8da645e101a8c20c6073efda3c7cc74eec01b87f Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:21:01 -0400 Subject: sctp: Get rid of an extra routing lookup when adding a transport. We used to perform 2 routing lookups for a new transport: one just for path mtu detection, and one to actually route to destination and path mtu update when sending a packet. There is no point in doing both of them, especially since the first one just for path mtu doesn't take into account source address and sometimes gives the wrong route, causing path mtu updates anyway. We now do just the one call to do both route to destination and get path mtu updates. Signed-off-by: Vlad Yasevich --- net/sctp/associola.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sctp/associola.c b/net/sctp/associola.c index caba989..8450960 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -680,13 +680,15 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, */ peer->param_flags = asoc->param_flags; + sctp_transport_route(peer, NULL, sp); + /* Initialize the pmtu of the transport. */ - if (peer->param_flags & SPP_PMTUD_ENABLE) - sctp_transport_pmtu(peer); - else if (asoc->pathmtu) - peer->pathmtu = asoc->pathmtu; - else - peer->pathmtu = SCTP_DEFAULT_MAXSEGMENT; + if (peer->param_flags & SPP_PMTUD_DISABLE) { + if (asoc->pathmtu) + peer->pathmtu = asoc->pathmtu; + else + peer->pathmtu = SCTP_DEFAULT_MAXSEGMENT; + } /* If this is the first transport addr on this association, * initialize the association PMTU to the peer's PMTU. -- cgit v1.1 From 723884339f90a9c420783135168cc1045750eb5d Mon Sep 17 00:00:00 2001 From: Bhaskar Dutta Date: Thu, 3 Sep 2009 17:25:47 +0530 Subject: sctp: Sysctl configuration for IPv4 Address Scoping This patch introduces a new sysctl option to make IPv4 Address Scoping configurable . In networking environments where DNAT rules in iptables prerouting chains convert destination IP's to link-local/private IP addresses, SCTP connections fail to establish as the INIT chunk is dropped by the kernel due to address scope match failure. For example to support overlapping IP addresses (same IP address with different vlan id) a Layer-5 application listens on link local IP's, and there is a DNAT rule that maps the destination IP to a link local IP. Such applications never get the SCTP INIT if the address-scoping draft is strictly followed. This sysctl configuration allows SCTP to function in such unconventional networking environments. Sysctl options: 0 - Disable IPv4 address scoping draft altogether 1 - Enable IPv4 address scoping (default, current behavior) 2 - Enable address scoping but allow IPv4 private addresses in init/init-ack 3 - Enable address scoping but allow IPv4 link local address in init/init-ack Signed-off-by: Bhaskar Dutta Signed-off-by: Vlad Yasevich --- net/sctp/bind_addr.c | 21 ++++++++++++++++++++- net/sctp/protocol.c | 11 ++++++----- net/sctp/sysctl.c | 12 ++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 6d5944a..13a6fba 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -510,9 +510,28 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) * of requested destination address, sender and receiver * SHOULD include all of its addresses with level greater * than or equal to L. + * + * Address scoping can be selectively controlled via sysctl + * option */ - if (addr_scope <= scope) + switch (sctp_scope_policy) { + case SCTP_SCOPE_POLICY_DISABLE: return 1; + case SCTP_SCOPE_POLICY_ENABLE: + if (addr_scope <= scope) + return 1; + break; + case SCTP_SCOPE_POLICY_PRIVATE: + if (addr_scope <= scope || SCTP_SCOPE_PRIVATE == addr_scope) + return 1; + break; + case SCTP_SCOPE_POLICY_LINK: + if (addr_scope <= scope || SCTP_SCOPE_LINK == addr_scope) + return 1; + break; + default: + break; + } return 0; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a76da65..60093be 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -431,16 +431,14 @@ static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp) * of requested destination address, sender and receiver * SHOULD include all of its addresses with level greater * than or equal to L. + * + * IPv4 scoping can be controlled through sysctl option + * net.sctp.addr_scope_policy */ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) { sctp_scope_t retval; - /* Should IPv4 scoping be a sysctl configurable option - * so users can turn it off (default on) for certain - * unconventional networking environments? - */ - /* Check for unusable SCTP addresses. */ if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr)) { retval = SCTP_SCOPE_UNUSABLE; @@ -1259,6 +1257,9 @@ SCTP_STATIC __init int sctp_init(void) /* Disable AUTH by default. */ sctp_auth_enable = 0; + /* Set SCOPE policy to enabled */ + sctp_scope_policy = SCTP_SCOPE_POLICY_ENABLE; + sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_address_families); diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 63eabbc..ab7151d 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -51,6 +51,7 @@ static int timer_max = 86400000; /* ms in one day */ static int int_max = INT_MAX; static int sack_timer_min = 1; static int sack_timer_max = 500; +static int addr_scope_max = 3; /* check sctp_scope_policy_t in include/net/sctp/constants.h for max entries */ extern int sysctl_sctp_mem[3]; extern int sysctl_sctp_rmem[3]; @@ -272,6 +273,17 @@ static ctl_table sctp_table[] = { .proc_handler = proc_dointvec, .strategy = sysctl_intvec }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "addr_scope_policy", + .data = &sctp_scope_policy, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &addr_scope_max, + }, { .ctl_name = 0 } }; -- cgit v1.1 From be2971438dec2e2d041af4701472a93a7dd03642 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 4 Sep 2009 14:34:06 +0800 Subject: sctp: remove dup code in net/sctp/output.c Use sctp_packet_reset() instead of dup code. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/output.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/sctp/output.c b/net/sctp/output.c index e47398c..5cbda8f 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -69,6 +69,17 @@ static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, struct sctp_chunk *chunk, u16 chunk_len); +static void sctp_packet_reset(struct sctp_packet *packet) +{ + packet->size = packet->overhead; + packet->has_cookie_echo = 0; + packet->has_sack = 0; + packet->has_data = 0; + packet->has_auth = 0; + packet->ipfragok = 0; + packet->auth = NULL; +} + /* Config a packet. * This appears to be a followup set of initializations. */ @@ -80,13 +91,8 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag); + sctp_packet_reset(packet); packet->vtag = vtag; - packet->has_cookie_echo = 0; - packet->has_sack = 0; - packet->has_auth = 0; - packet->has_data = 0; - packet->ipfragok = 0; - packet->auth = NULL; if (ecn_capable && sctp_packet_empty(packet)) { chunk = sctp_get_ecne_prepend(packet->transport->asoc); @@ -124,29 +130,12 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, } overhead += sizeof(struct sctphdr); packet->overhead = overhead; - packet->size = overhead; + sctp_packet_reset(packet); packet->vtag = 0; - packet->has_cookie_echo = 0; - packet->has_sack = 0; - packet->has_auth = 0; - packet->has_data = 0; - packet->ipfragok = 0; packet->malloced = 0; - packet->auth = NULL; return packet; } -static void sctp_packet_reset(struct sctp_packet *packet) -{ - packet->size = packet->overhead; - packet->has_cookie_echo = 0; - packet->has_sack = 0; - packet->has_data = 0; - packet->has_auth = 0; - packet->ipfragok = 0; - packet->auth = NULL; -} - /* Free a packet. */ void sctp_packet_free(struct sctp_packet *packet) { -- cgit v1.1 From f1751c57f7bb816c9b6b4cb5d79c703aaa7199da Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 4 Sep 2009 18:21:03 -0400 Subject: sctp: Catch bogus stream sequence numbers Since our TSN map is capable of holding at most a 4K chunk gap, there is no way that during this gap, a stream sequence number (unsigned short) can wrap such that the new number is smaller then the next expected one. If such a case is encountered, this is a protocol violation. Signed-off-by: Vlad Yasevich --- net/sctp/sm_statefuns.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index a7f18a3..c8fae19 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2891,6 +2891,9 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, goto discard_force; case SCTP_IERROR_NO_DATA: goto consume; + case SCTP_IERROR_PROTO_VIOLATION: + return sctp_sf_abort_violation(ep, asoc, chunk, commands, + (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); } @@ -3001,6 +3004,9 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, break; case SCTP_IERROR_NO_DATA: goto consume; + case SCTP_IERROR_PROTO_VIOLATION: + return sctp_sf_abort_violation(ep, asoc, chunk, commands, + (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t)); default: BUG(); } @@ -5877,6 +5883,9 @@ static int sctp_eat_data(const struct sctp_association *asoc, __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; + u16 ssn; + u16 sid; + u8 ordered = 0; data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); @@ -6016,8 +6025,10 @@ static int sctp_eat_data(const struct sctp_association *asoc, */ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS); - else + else { SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS); + ordered = 1; + } /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number * @@ -6027,7 +6038,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) * and discard the DATA chunk. */ - if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + sid = ntohs(data_hdr->stream); + if (sid >= asoc->c.sinit_max_instreams) { /* Mark tsn as received even though we drop it */ sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); @@ -6040,6 +6052,18 @@ static int sctp_eat_data(const struct sctp_association *asoc, return SCTP_IERROR_BAD_STREAM; } + /* Check to see if the SSN is possible for this TSN. + * The biggest gap we can record is 4K wide. Since SSNs wrap + * at an unsigned short, there is no way that an SSN can + * wrap and for a valid TSN. We can simply check if the current + * SSN is smaller then the next expected one. If it is, it wrapped + * and is invalid. + */ + ssn = ntohs(data_hdr->ssn); + if (ordered && SSN_lt(ssn, sctp_ssn_peek(&asoc->ssnmap->in, sid))) { + return SCTP_IERROR_PROTO_VIOLATION; + } + /* Send the data up to the user. Note: Schedule the * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK * chunk needs the updated rwnd. -- cgit v1.1 From b1f57195585e376d1944c32c046359640b06a669 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Fri, 4 Sep 2009 20:36:52 -0700 Subject: netlink: silence compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC net/netlink/genetlink.o net/netlink/genetlink.c: In function ‘genl_register_mc_group’: net/netlink/genetlink.c:139: warning: ‘err’ may be used uninitialized in this function From following the code 'err' is initialized, but set it to zero to silence the warning. Signed-off-by: Brian Haley Signed-off-by: David S. Miller --- net/netlink/genetlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 575c643..66f6ba0 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -136,7 +136,7 @@ int genl_register_mc_group(struct genl_family *family, { int id; unsigned long *new_groups; - int err; + int err = 0; BUG_ON(grp->name[0] == '\0'); -- cgit v1.1 From c9f1d0389b962521af1e2b699c8ee5e299d77b85 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:13 +0000 Subject: net_sched: fix class grafting errno codes If the parent qdisc doesn't support classes, use EOPNOTSUPP. If the parent class doesn't exist, use ENOENT. Currently EINVAL is returned in both cases. Additionally check whether grafting is supported and remove a now unnecessary graft function from sch_ingress. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_api.c | 8 ++++---- net/sched/sch_ingress.c | 7 ------- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 24d17ce..bef2d64 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -728,14 +728,14 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, } else { const struct Qdisc_class_ops *cops = parent->ops->cl_ops; - err = -EINVAL; - - if (cops) { + err = -EOPNOTSUPP; + if (cops && cops->graft) { unsigned long cl = cops->get(parent, classid); if (cl) { err = cops->graft(parent, cl, new, &old); cops->put(parent, cl); - } + } else + err = -ENOENT; } if (!err) notify_and_destroy(skb, n, classid, old, new); diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 4a2b773..ace7902 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -22,12 +22,6 @@ struct ingress_qdisc_data { /* ------------------------- Class/flow operations ------------------------- */ -static int ingress_graft(struct Qdisc *sch, unsigned long arg, - struct Qdisc *new, struct Qdisc **old) -{ - return -EOPNOTSUPP; -} - static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) { return NULL; @@ -123,7 +117,6 @@ nla_put_failure: } static const struct Qdisc_class_ops ingress_class_ops = { - .graft = ingress_graft, .leaf = ingress_leaf, .get = ingress_get, .put = ingress_put, -- cgit v1.1 From 71ebe5e91947392bc276af713827eab12b6db8e4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:15 +0000 Subject: net_sched: make cls_ops->tcf_chain() optional Some qdiscs don't support attaching filters. Handle this centrally in cls_api and return a proper errno code (EOPNOTSUPP) instead of EINVAL. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/cls_api.c | 5 +++++ net/sched/sch_red.c | 6 ------ net/sched/sch_tbf.c | 6 ------ 3 files changed, 5 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 09cdcdf..bcfbdb4 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -181,6 +181,9 @@ replay: if ((cops = q->ops->cl_ops) == NULL) return -EINVAL; + if (cops->tcf_chain == NULL) + return -EOPNOTSUPP; + /* Do we search for filter, attached to class? */ if (TC_H_MIN(parent)) { cl = cops->get(q, parent); @@ -433,6 +436,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) goto out; if ((cops = q->ops->cl_ops) == NULL) goto errout; + if (cops->tcf_chain == NULL) + goto errout; if (TC_H_MIN(tcm->tcm_parent)) { cl = cops->get(q, tcm->tcm_parent); if (cl == 0) diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 2bdf241..c27b802 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -331,11 +331,6 @@ static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) } } -static struct tcf_proto **red_find_tcf(struct Qdisc *sch, unsigned long cl) -{ - return NULL; -} - static const struct Qdisc_class_ops red_class_ops = { .graft = red_graft, .leaf = red_leaf, @@ -344,7 +339,6 @@ static const struct Qdisc_class_ops red_class_ops = { .change = red_change_class, .delete = red_delete, .walk = red_walk, - .tcf_chain = red_find_tcf, .dump = red_dump_class, }; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index e22dfe8..2890969 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -433,11 +433,6 @@ static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) } } -static struct tcf_proto **tbf_find_tcf(struct Qdisc *sch, unsigned long cl) -{ - return NULL; -} - static const struct Qdisc_class_ops tbf_class_ops = { .graft = tbf_graft, @@ -447,7 +442,6 @@ static const struct Qdisc_class_ops tbf_class_ops = .change = tbf_change_class, .delete = tbf_delete, .walk = tbf_walk, - .tcf_chain = tbf_find_tcf, .dump = tbf_dump_class, }; -- cgit v1.1 From de6d5cdf881353f83006d5f3e28ac4fffd42145e Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:16 +0000 Subject: net_sched: make cls_ops->change and cls_ops->delete optional Some schedulers don't support creating, changing or deleting classes. Make the respective callbacks optionally and consistently return -EOPNOTSUPP for unsupported operations, instead of currently either -EOPNOTSUPP, -ENOSYS or no error. In case of sch_prio and sch_multiq, the removed operations additionally checked for an invalid class. This is not necessary since the class argument can only orginate from ->get() or in case of ->change is 0 for creation of new classes, in which case ->change() incorrectly returned -ENOENT. As a side-effect, this patch fixes a possible (root-only) NULL pointer function call in sch_ingress, which didn't implement a so far mandatory ->delete() operation. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_api.c | 8 ++++++-- net/sched/sch_ingress.c | 7 ------- net/sched/sch_multiq.c | 22 ---------------------- net/sched/sch_prio.c | 21 --------------------- net/sched/sch_red.c | 13 ------------- net/sched/sch_sfq.c | 7 ------- net/sched/sch_tbf.c | 13 ------------- 7 files changed, 6 insertions(+), 85 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index bef2d64..166fcca 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1417,7 +1417,9 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) goto out; break; case RTM_DELTCLASS: - err = cops->delete(q, cl); + err = -EOPNOTSUPP; + if (cops->delete) + err = cops->delete(q, cl); if (err == 0) tclass_notify(skb, n, q, cl, RTM_DELTCLASS); goto out; @@ -1431,7 +1433,9 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) } new_cl = cl; - err = cops->change(q, clid, pid, tca, &new_cl); + err = -EOPNOTSUPP; + if (cops->change) + err = cops->change(q, clid, pid, tca, &new_cl); if (err == 0) tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS); diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index ace7902..a9e646b 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -42,12 +42,6 @@ static void ingress_put(struct Qdisc *sch, unsigned long cl) { } -static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent, - struct nlattr **tca, unsigned long *arg) -{ - return 0; -} - static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker) { return; @@ -120,7 +114,6 @@ static const struct Qdisc_class_ops ingress_class_ops = { .leaf = ingress_leaf, .get = ingress_get, .put = ingress_put, - .change = ingress_change, .walk = ingress_walk, .tcf_chain = ingress_find_tcf, .bind_tcf = ingress_bind_filter, diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 9127312..a0ffe71 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -348,26 +348,6 @@ static void multiq_put(struct Qdisc *q, unsigned long cl) return; } -static int multiq_change(struct Qdisc *sch, u32 handle, u32 parent, - struct nlattr **tca, unsigned long *arg) -{ - unsigned long cl = *arg; - struct multiq_sched_data *q = qdisc_priv(sch); - - if (cl - 1 > q->bands) - return -ENOENT; - return 0; -} - -static int multiq_delete(struct Qdisc *sch, unsigned long cl) -{ - struct multiq_sched_data *q = qdisc_priv(sch); - if (cl - 1 > q->bands) - return -ENOENT; - return 0; -} - - static int multiq_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { @@ -430,8 +410,6 @@ static const struct Qdisc_class_ops multiq_class_ops = { .leaf = multiq_leaf, .get = multiq_get, .put = multiq_put, - .change = multiq_change, - .delete = multiq_delete, .walk = multiq_walk, .tcf_chain = multiq_find_tcf, .bind_tcf = multiq_bind, diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 94cecef..209a4ca 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -311,25 +311,6 @@ static void prio_put(struct Qdisc *q, unsigned long cl) return; } -static int prio_change(struct Qdisc *sch, u32 handle, u32 parent, struct nlattr **tca, unsigned long *arg) -{ - unsigned long cl = *arg; - struct prio_sched_data *q = qdisc_priv(sch); - - if (cl - 1 > q->bands) - return -ENOENT; - return 0; -} - -static int prio_delete(struct Qdisc *sch, unsigned long cl) -{ - struct prio_sched_data *q = qdisc_priv(sch); - if (cl - 1 > q->bands) - return -ENOENT; - return 0; -} - - static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { @@ -392,8 +373,6 @@ static const struct Qdisc_class_ops prio_class_ops = { .leaf = prio_leaf, .get = prio_get, .put = prio_put, - .change = prio_change, - .delete = prio_delete, .walk = prio_walk, .tcf_chain = prio_find_tcf, .bind_tcf = prio_bind, diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index c27b802..a2c4d1a 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -308,17 +308,6 @@ static void red_put(struct Qdisc *sch, unsigned long arg) return; } -static int red_change_class(struct Qdisc *sch, u32 classid, u32 parentid, - struct nlattr **tca, unsigned long *arg) -{ - return -ENOSYS; -} - -static int red_delete(struct Qdisc *sch, unsigned long cl) -{ - return -ENOSYS; -} - static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { @@ -336,8 +325,6 @@ static const struct Qdisc_class_ops red_class_ops = { .leaf = red_leaf, .get = red_get, .put = red_put, - .change = red_change_class, - .delete = red_delete, .walk = red_walk, .dump = red_dump_class, }; diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 8706920..cb21380 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -496,12 +496,6 @@ nla_put_failure: return -1; } -static int sfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, - struct nlattr **tca, unsigned long *arg) -{ - return -EOPNOTSUPP; -} - static unsigned long sfq_get(struct Qdisc *sch, u32 classid) { return 0; @@ -560,7 +554,6 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg) static const struct Qdisc_class_ops sfq_class_ops = { .get = sfq_get, - .change = sfq_change_class, .tcf_chain = sfq_find_tcf, .dump = sfq_dump_class, .dump_stats = sfq_dump_class_stats, diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 2890969..d904167 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -410,17 +410,6 @@ static void tbf_put(struct Qdisc *sch, unsigned long arg) { } -static int tbf_change_class(struct Qdisc *sch, u32 classid, u32 parentid, - struct nlattr **tca, unsigned long *arg) -{ - return -ENOSYS; -} - -static int tbf_delete(struct Qdisc *sch, unsigned long arg) -{ - return -ENOSYS; -} - static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { @@ -439,8 +428,6 @@ static const struct Qdisc_class_ops tbf_class_ops = .leaf = tbf_leaf, .get = tbf_get, .put = tbf_put, - .change = tbf_change_class, - .delete = tbf_delete, .walk = tbf_walk, .dump = tbf_dump_class, }; -- cgit v1.1 From 5b9a9ccfad8553dbf7a9b17ba78bad70215ed0e2 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:17 +0000 Subject: net_sched: remove some unnecessary checks in classful schedulers The class argument to the ->graft(), ->leaf(), ->dump(), ->dump_stats() all originate from either ->get() or ->walk() and are always valid. Remove unnecessary checks. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_cbq.c | 38 +++++++++++++++++--------------------- net/sched/sch_hfsc.c | 4 +--- net/sched/sch_htb.c | 35 +++++++++++++++++------------------ net/sched/sch_multiq.c | 11 +---------- net/sched/sch_prio.c | 11 +---------- net/sched/sch_red.c | 2 -- net/sched/sch_tbf.c | 3 --- 7 files changed, 37 insertions(+), 67 deletions(-) (limited to 'net') diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index d5798e1..5b132c4 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1621,29 +1621,25 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, { struct cbq_class *cl = (struct cbq_class*)arg; - if (cl) { - if (new == NULL) { - new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, - &pfifo_qdisc_ops, - cl->common.classid); - if (new == NULL) - return -ENOBUFS; - } else { + if (new == NULL) { + new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, + &pfifo_qdisc_ops, cl->common.classid); + if (new == NULL) + return -ENOBUFS; + } else { #ifdef CONFIG_NET_CLS_ACT - if (cl->police == TC_POLICE_RECLASSIFY) - new->reshape_fail = cbq_reshape_fail; + if (cl->police == TC_POLICE_RECLASSIFY) + new->reshape_fail = cbq_reshape_fail; #endif - } - sch_tree_lock(sch); - *old = cl->q; - cl->q = new; - qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); - qdisc_reset(*old); - sch_tree_unlock(sch); - - return 0; } - return -ENOENT; + sch_tree_lock(sch); + *old = cl->q; + cl->q = new; + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); + qdisc_reset(*old); + sch_tree_unlock(sch); + + return 0; } static struct Qdisc * @@ -1651,7 +1647,7 @@ cbq_leaf(struct Qdisc *sch, unsigned long arg) { struct cbq_class *cl = (struct cbq_class*)arg; - return cl ? cl->q : NULL; + return cl->q; } static void cbq_qlen_notify(struct Qdisc *sch, unsigned long arg) diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index dad0144..375d64c 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1203,8 +1203,6 @@ hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, { struct hfsc_class *cl = (struct hfsc_class *)arg; - if (cl == NULL) - return -ENOENT; if (cl->level > 0) return -EINVAL; if (new == NULL) { @@ -1228,7 +1226,7 @@ hfsc_class_leaf(struct Qdisc *sch, unsigned long arg) { struct hfsc_class *cl = (struct hfsc_class *)arg; - if (cl != NULL && cl->level == 0) + if (cl->level == 0) return cl->qdisc; return NULL; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index ec4d463..85acab9 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1117,30 +1117,29 @@ static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, { struct htb_class *cl = (struct htb_class *)arg; - if (cl && !cl->level) { - if (new == NULL && - (new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, - &pfifo_qdisc_ops, - cl->common.classid)) - == NULL) - return -ENOBUFS; - sch_tree_lock(sch); - *old = cl->un.leaf.q; - cl->un.leaf.q = new; - if (*old != NULL) { - qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); - qdisc_reset(*old); - } - sch_tree_unlock(sch); - return 0; + if (cl->level) + return -EINVAL; + if (new == NULL && + (new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue, + &pfifo_qdisc_ops, + cl->common.classid)) == NULL) + return -ENOBUFS; + + sch_tree_lock(sch); + *old = cl->un.leaf.q; + cl->un.leaf.q = new; + if (*old != NULL) { + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); + qdisc_reset(*old); } - return -ENOENT; + sch_tree_unlock(sch); + return 0; } static struct Qdisc *htb_leaf(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; - return (cl && !cl->level) ? cl->un.leaf.q : NULL; + return !cl->level ? cl->un.leaf.q : NULL; } static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index a0ffe71..069f81c 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -298,9 +298,6 @@ static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct multiq_sched_data *q = qdisc_priv(sch); unsigned long band = arg - 1; - if (band >= q->bands) - return -EINVAL; - if (new == NULL) new = &noop_qdisc; @@ -320,9 +317,6 @@ multiq_leaf(struct Qdisc *sch, unsigned long arg) struct multiq_sched_data *q = qdisc_priv(sch); unsigned long band = arg - 1; - if (band >= q->bands) - return NULL; - return q->queues[band]; } @@ -353,11 +347,8 @@ static int multiq_dump_class(struct Qdisc *sch, unsigned long cl, { struct multiq_sched_data *q = qdisc_priv(sch); - if (cl - 1 > q->bands) - return -ENOENT; tcm->tcm_handle |= TC_H_MIN(cl); - if (q->queues[cl-1]) - tcm->tcm_info = q->queues[cl-1]->handle; + tcm->tcm_info = q->queues[cl-1]->handle; return 0; } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 209a4ca..0f73c41 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -262,9 +262,6 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct prio_sched_data *q = qdisc_priv(sch); unsigned long band = arg - 1; - if (band >= q->bands) - return -EINVAL; - if (new == NULL) new = &noop_qdisc; @@ -284,9 +281,6 @@ prio_leaf(struct Qdisc *sch, unsigned long arg) struct prio_sched_data *q = qdisc_priv(sch); unsigned long band = arg - 1; - if (band >= q->bands) - return NULL; - return q->queues[band]; } @@ -316,11 +310,8 @@ static int prio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff * { struct prio_sched_data *q = qdisc_priv(sch); - if (cl - 1 > q->bands) - return -ENOENT; tcm->tcm_handle |= TC_H_MIN(cl); - if (q->queues[cl-1]) - tcm->tcm_info = q->queues[cl-1]->handle; + tcm->tcm_info = q->queues[cl-1]->handle; return 0; } diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index a2c4d1a..072cdf4 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -268,8 +268,6 @@ static int red_dump_class(struct Qdisc *sch, unsigned long cl, { struct red_sched_data *q = qdisc_priv(sch); - if (cl != 1) - return -ENOENT; tcm->tcm_handle |= TC_H_MIN(1); tcm->tcm_info = q->qdisc->handle; return 0; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index d904167..8fb8107 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -368,9 +368,6 @@ static int tbf_dump_class(struct Qdisc *sch, unsigned long cl, { struct tbf_sched_data *q = qdisc_priv(sch); - if (cl != 1) /* only one class */ - return -ENOENT; - tcm->tcm_handle |= TC_H_MIN(1); tcm->tcm_info = q->qdisc->handle; -- cgit v1.1 From af356afa010f3cd2c8b8fcc3bce90f7a7b7ec02a Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:18 +0000 Subject: net_sched: reintroduce dev->qdisc for use by sch_api Currently the multiqueue integration with the qdisc API suffers from a few problems: - with multiple queues, all root qdiscs use the same handle. This means they can't be exposed to userspace in a backwards compatible fashion. - all API operations always refer to queue number 0. Newly created qdiscs are automatically shared between all queues, its not possible to address individual queues or restore multiqueue behaviour once a shared qdisc has been attached. - Dumps only contain the root qdisc of queue 0, in case of non-shared qdiscs this means the statistics are incomplete. This patch reintroduces dev->qdisc, which points to the (single) root qdisc from userspace's point of view. Currently it either points to the first (non-shared) default qdisc, or a qdisc shared between all queues. The following patches will introduce a classful dummy qdisc, which will be used as root qdisc and contain the per-queue qdiscs as children. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 6 ++---- net/sched/cls_api.c | 7 ++----- net/sched/sch_api.c | 41 ++++++++++++++++------------------------- net/sched/sch_generic.c | 25 +++++++++++-------------- 4 files changed, 31 insertions(+), 48 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bbcba2a..eb42873 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -606,7 +606,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags) { - struct netdev_queue *txq; struct ifinfomsg *ifm; struct nlmsghdr *nlh; const struct net_device_stats *stats; @@ -637,9 +636,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (dev->master) NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); - txq = netdev_get_tx_queue(dev, 0); - if (txq->qdisc_sleeping) - NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); + if (dev->qdisc) + NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc->ops->id); if (dev->ifalias) NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index bcfbdb4..6a53694 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -168,8 +168,7 @@ replay: /* Find qdisc */ if (!parent) { - struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); - q = dev_queue->qdisc_sleeping; + q = dev->qdisc; parent = q->handle; } else { q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); @@ -408,7 +407,6 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -427,9 +425,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return skb->len; - dev_queue = netdev_get_tx_queue(dev, 0); if (!tcm->tcm_parent) - q = dev_queue->qdisc_sleeping; + q = dev->qdisc; else q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); if (!q) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 166fcca..8aa9a0c 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -207,7 +207,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) static void qdisc_list_add(struct Qdisc *q) { if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) - list_add_tail(&q->list, &qdisc_root_sleeping(q)->list); + list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list); } void qdisc_list_del(struct Qdisc *q) @@ -219,17 +219,11 @@ EXPORT_SYMBOL(qdisc_list_del); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { - unsigned int i; struct Qdisc *q; - for (i = 0; i < dev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - struct Qdisc *txq_root = txq->qdisc_sleeping; - - q = qdisc_match_from_root(txq_root, handle); - if (q) - goto out; - } + q = qdisc_match_from_root(dev->qdisc, handle); + if (q) + goto out; q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle); out: @@ -720,9 +714,14 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, if (new && i > 0) atomic_inc(&new->refcnt); - notify_and_destroy(skb, n, classid, old, new); + qdisc_destroy(old); } + notify_and_destroy(skb, n, classid, dev->qdisc, new); + if (new) + atomic_inc(&new->refcnt); + dev->qdisc = new ? : &noop_qdisc; + if (dev->flags & IFF_UP) dev_activate(dev); } else { @@ -974,9 +973,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) q = dev->rx_queue.qdisc_sleeping; } } else { - struct netdev_queue *dev_queue; - dev_queue = netdev_get_tx_queue(dev, 0); - q = dev_queue->qdisc_sleeping; + q = dev->qdisc; } if (!q) return -ENOENT; @@ -1044,9 +1041,7 @@ replay: q = dev->rx_queue.qdisc_sleeping; } } else { - struct netdev_queue *dev_queue; - dev_queue = netdev_get_tx_queue(dev, 0); - q = dev_queue->qdisc_sleeping; + q = dev->qdisc; } /* It may be default qdisc, ignore it */ @@ -1291,8 +1286,7 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) s_q_idx = 0; q_idx = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - if (tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, &q_idx, s_q_idx) < 0) + if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx) < 0) goto done; dev_queue = &dev->rx_queue; @@ -1323,7 +1317,6 @@ done: static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { struct net *net = sock_net(skb->sk); - struct netdev_queue *dev_queue; struct tcmsg *tcm = NLMSG_DATA(n); struct nlattr *tca[TCA_MAX + 1]; struct net_device *dev; @@ -1361,7 +1354,6 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ - dev_queue = netdev_get_tx_queue(dev, 0); if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); @@ -1372,7 +1364,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) } else if (qid1) { qid = qid1; } else if (qid == 0) - qid = dev_queue->qdisc_sleeping->handle; + qid = dev->qdisc->handle; /* Now qid is genuine qdisc handle consistent both with parent and child. @@ -1383,7 +1375,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) pid = TC_H_MAKE(qid, pid); } else { if (qid == 0) - qid = dev_queue->qdisc_sleeping->handle; + qid = dev->qdisc->handle; } /* OK. Locate qdisc */ @@ -1588,8 +1580,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - if (tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb, &t, s_t) < 0) + if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0) goto done; dev_queue = &dev->rx_queue; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6128e6f..a91f079 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -623,19 +623,6 @@ void qdisc_destroy(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_destroy); -static bool dev_all_qdisc_sleeping_noop(struct net_device *dev) -{ - unsigned int i; - - for (i = 0; i < dev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - - if (txq->qdisc_sleeping != &noop_qdisc) - return false; - } - return true; -} - static void attach_one_default_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_unused) @@ -677,6 +664,7 @@ static void transition_one_qdisc(struct net_device *dev, void dev_activate(struct net_device *dev) { + struct netdev_queue *txq; int need_watchdog; /* No queueing discipline is attached to device; @@ -685,9 +673,14 @@ void dev_activate(struct net_device *dev) virtual interfaces */ - if (dev_all_qdisc_sleeping_noop(dev)) + if (dev->qdisc == &noop_qdisc) { netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); + txq = netdev_get_tx_queue(dev, 0); + dev->qdisc = txq->qdisc_sleeping; + atomic_inc(&dev->qdisc->refcnt); + } + if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ return; @@ -777,6 +770,7 @@ static void dev_init_scheduler_queue(struct net_device *dev, void dev_init_scheduler(struct net_device *dev) { + dev->qdisc = &noop_qdisc; netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); @@ -802,5 +796,8 @@ void dev_shutdown(struct net_device *dev) { netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); + qdisc_destroy(dev->qdisc); + dev->qdisc = &noop_qdisc; + WARN_ON(timer_pending(&dev->watchdog_timer)); } -- cgit v1.1 From 589983cd21f4a2e4ed74a958805a90fa676845c5 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 4 Sep 2009 06:41:20 +0000 Subject: net_sched: move dev_graft_qdisc() to sch_generic.c It will be used in a following patch by the multiqueue qdisc. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_api.c | 26 -------------------------- net/sched/sch_generic.c | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 8aa9a0c..d71f12b 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -610,32 +610,6 @@ static u32 qdisc_alloc_handle(struct net_device *dev) return i>0 ? autohandle : 0; } -/* Attach toplevel qdisc to device queue. */ - -static struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, - struct Qdisc *qdisc) -{ - struct Qdisc *oqdisc = dev_queue->qdisc_sleeping; - spinlock_t *root_lock; - - root_lock = qdisc_lock(oqdisc); - spin_lock_bh(root_lock); - - /* Prune old scheduler */ - if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) - qdisc_reset(oqdisc); - - /* ... and graft new one */ - if (qdisc == NULL) - qdisc = &noop_qdisc; - dev_queue->qdisc_sleeping = qdisc; - rcu_assign_pointer(dev_queue->qdisc, &noop_qdisc); - - spin_unlock_bh(root_lock); - - return oqdisc; -} - void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) { const struct Qdisc_class_ops *cops; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a91f079..e7c47ce 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -623,6 +623,31 @@ void qdisc_destroy(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_destroy); +/* Attach toplevel qdisc to device queue. */ +struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, + struct Qdisc *qdisc) +{ + struct Qdisc *oqdisc = dev_queue->qdisc_sleeping; + spinlock_t *root_lock; + + root_lock = qdisc_lock(oqdisc); + spin_lock_bh(root_lock); + + /* Prune old scheduler */ + if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) + qdisc_reset(oqdisc); + + /* ... and graft new one */ + if (qdisc == NULL) + qdisc = &noop_qdisc; + dev_queue->qdisc_sleeping = qdisc; + rcu_assign_pointer(dev_queue->qdisc, &noop_qdisc); + + spin_unlock_bh(root_lock); + + return oqdisc; +} + static void attach_one_default_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_unused) -- cgit v1.1 From 6ec1c69a8f6492fd25722f4762721921da074c12 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 6 Sep 2009 01:58:51 -0700 Subject: net_sched: add classful multiqueue dummy scheduler This patch adds a classful dummy scheduler which can be used as root qdisc for multiqueue devices and exposes each device queue as a child class. This allows to address queues individually and graft them similar to regular classes. Additionally it presents an accumulated view of the statistics of all real root qdiscs in the dummy root. Two new callbacks are added to the qdisc_ops and qdisc_class_ops: - cl_ops->select_queue selects the tx queue number for new child classes. - qdisc_ops->attach() overrides root qdisc device grafting to attach non-shared qdiscs to the queues. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/Makefile | 2 +- net/sched/sch_api.c | 18 +++- net/sched/sch_generic.c | 32 +++++-- net/sched/sch_mq.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 273 insertions(+), 13 deletions(-) create mode 100644 net/sched/sch_mq.c (limited to 'net') diff --git a/net/sched/Makefile b/net/sched/Makefile index 54d950c..f14e71b 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -2,7 +2,7 @@ # Makefile for the Linux Traffic Control Unit. # -obj-y := sch_generic.o +obj-y := sch_generic.o sch_mq.o obj-$(CONFIG_NET_SCHED) += sch_api.o sch_blackhole.o obj-$(CONFIG_NET_CLS) += cls_api.o diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index d71f12b..2a78d54 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -678,6 +678,11 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, if (dev->flags & IFF_UP) dev_deactivate(dev); + if (new && new->ops->attach) { + new->ops->attach(new); + num_q = 0; + } + for (i = 0; i < num_q; i++) { struct netdev_queue *dev_queue = &dev->rx_queue; @@ -692,7 +697,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, } notify_and_destroy(skb, n, classid, dev->qdisc, new); - if (new) + if (new && !new->ops->attach) atomic_inc(&new->refcnt); dev->qdisc = new ? : &noop_qdisc; @@ -1095,10 +1100,16 @@ create_n_graft: q = qdisc_create(dev, &dev->rx_queue, tcm->tcm_parent, tcm->tcm_parent, tca, &err); - else - q = qdisc_create(dev, netdev_get_tx_queue(dev, 0), + else { + unsigned int ntx = 0; + + if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue) + ntx = p->ops->cl_ops->select_queue(p, tcm); + + q = qdisc_create(dev, netdev_get_tx_queue(dev, ntx), tcm->tcm_parent, tcm->tcm_handle, tca, &err); + } if (q == NULL) { if (err == -EAGAIN) goto replay; @@ -1674,6 +1685,7 @@ static int __init pktsched_init(void) { register_qdisc(&pfifo_qdisc_ops); register_qdisc(&bfifo_qdisc_ops); + register_qdisc(&mq_qdisc_ops); proc_net_fops_create(&init_net, "psched", 0, &psched_fops); rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index e7c47ce..4ae6aa5 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -514,7 +514,7 @@ static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt) return 0; } -static struct Qdisc_ops pfifo_fast_ops __read_mostly = { +struct Qdisc_ops pfifo_fast_ops __read_mostly = { .id = "pfifo_fast", .priv_size = sizeof(struct pfifo_fast_priv), .enqueue = pfifo_fast_enqueue, @@ -670,6 +670,26 @@ static void attach_one_default_qdisc(struct net_device *dev, dev_queue->qdisc_sleeping = qdisc; } +static void attach_default_qdiscs(struct net_device *dev) +{ + struct netdev_queue *txq; + struct Qdisc *qdisc; + + txq = netdev_get_tx_queue(dev, 0); + + if (!netif_is_multiqueue(dev) || dev->tx_queue_len == 0) { + netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); + dev->qdisc = txq->qdisc_sleeping; + atomic_inc(&dev->qdisc->refcnt); + } else { + qdisc = qdisc_create_dflt(dev, txq, &mq_qdisc_ops, TC_H_ROOT); + if (qdisc) { + qdisc->ops->attach(qdisc); + dev->qdisc = qdisc; + } + } +} + static void transition_one_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_need_watchdog) @@ -689,7 +709,6 @@ static void transition_one_qdisc(struct net_device *dev, void dev_activate(struct net_device *dev) { - struct netdev_queue *txq; int need_watchdog; /* No queueing discipline is attached to device; @@ -698,13 +717,8 @@ void dev_activate(struct net_device *dev) virtual interfaces */ - if (dev->qdisc == &noop_qdisc) { - netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); - - txq = netdev_get_tx_queue(dev, 0); - dev->qdisc = txq->qdisc_sleeping; - atomic_inc(&dev->qdisc->refcnt); - } + if (dev->qdisc == &noop_qdisc) + attach_default_qdiscs(dev); if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c new file mode 100644 index 0000000..c84dec9 --- /dev/null +++ b/net/sched/sch_mq.c @@ -0,0 +1,234 @@ +/* + * net/sched/sch_mq.c Classful multiqueue dummy scheduler + * + * Copyright (c) 2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct mq_sched { + struct Qdisc **qdiscs; +}; + +static void mq_destroy(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); + unsigned int ntx; + + if (!priv->qdiscs) + return; + for (ntx = 0; ntx < dev->num_tx_queues && priv->qdiscs[ntx]; ntx++) + qdisc_destroy(priv->qdiscs[ntx]); + kfree(priv->qdiscs); +} + +static int mq_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); + struct netdev_queue *dev_queue; + struct Qdisc *qdisc; + unsigned int ntx; + + if (sch->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + if (!netif_is_multiqueue(dev)) + return -EOPNOTSUPP; + + /* pre-allocate qdiscs, attachment can't fail */ + priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), + GFP_KERNEL); + if (priv->qdiscs == NULL) + return -ENOMEM; + + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + dev_queue = netdev_get_tx_queue(dev, ntx); + qdisc = qdisc_create_dflt(dev, dev_queue, &pfifo_fast_ops, + TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(ntx + 1))); + if (qdisc == NULL) + goto err; + qdisc->flags |= TCQ_F_CAN_BYPASS; + priv->qdiscs[ntx] = qdisc; + } + + return 0; + +err: + mq_destroy(sch); + return -ENOMEM; +} + +static void mq_attach(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct mq_sched *priv = qdisc_priv(sch); + struct Qdisc *qdisc; + unsigned int ntx; + + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + qdisc = priv->qdiscs[ntx]; + qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); + if (qdisc) + qdisc_destroy(qdisc); + } + kfree(priv->qdiscs); + priv->qdiscs = NULL; +} + +static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct net_device *dev = qdisc_dev(sch); + struct Qdisc *qdisc; + unsigned int ntx; + + sch->q.qlen = 0; + memset(&sch->bstats, 0, sizeof(sch->bstats)); + memset(&sch->qstats, 0, sizeof(sch->qstats)); + + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; + spin_lock_bh(qdisc_lock(qdisc)); + sch->q.qlen += qdisc->q.qlen; + sch->bstats.bytes += qdisc->bstats.bytes; + sch->bstats.packets += qdisc->bstats.packets; + sch->qstats.qlen += qdisc->qstats.qlen; + sch->qstats.backlog += qdisc->qstats.backlog; + sch->qstats.drops += qdisc->qstats.drops; + sch->qstats.requeues += qdisc->qstats.requeues; + sch->qstats.overlimits += qdisc->qstats.overlimits; + spin_unlock_bh(qdisc_lock(qdisc)); + } + return 0; +} + +static struct netdev_queue *mq_queue_get(struct Qdisc *sch, unsigned long cl) +{ + struct net_device *dev = qdisc_dev(sch); + unsigned long ntx = cl - 1; + + if (ntx >= dev->num_tx_queues) + return NULL; + return netdev_get_tx_queue(dev, ntx); +} + +static unsigned int mq_select_queue(struct Qdisc *sch, struct tcmsg *tcm) +{ + unsigned int ntx = TC_H_MIN(tcm->tcm_parent); + + if (!mq_queue_get(sch, ntx)) + return 0; + return ntx - 1; +} + +static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, + struct Qdisc **old) +{ + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + struct net_device *dev = qdisc_dev(sch); + + if (dev->flags & IFF_UP) + dev_deactivate(dev); + + *old = dev_graft_qdisc(dev_queue, new); + + if (dev->flags & IFF_UP) + dev_activate(dev); + return 0; +} + +static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) +{ + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + + return dev_queue->qdisc_sleeping; +} + +static unsigned long mq_get(struct Qdisc *sch, u32 classid) +{ + unsigned int ntx = TC_H_MIN(classid); + + if (!mq_queue_get(sch, ntx)) + return 0; + return ntx; +} + +static void mq_put(struct Qdisc *sch, unsigned long cl) +{ + return; +} + +static int mq_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + + tcm->tcm_parent = TC_H_ROOT; + tcm->tcm_handle |= TC_H_MIN(cl); + tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + return 0; +} + +static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, + struct gnet_dump *d) +{ + struct netdev_queue *dev_queue = mq_queue_get(sch, cl); + + sch = dev_queue->qdisc_sleeping; + if (gnet_stats_copy_basic(d, &sch->bstats) < 0 || + gnet_stats_copy_queue(d, &sch->qstats) < 0) + return -1; + return 0; +} + +static void mq_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + struct net_device *dev = qdisc_dev(sch); + unsigned int ntx; + + if (arg->stop) + return; + + arg->count = arg->skip; + for (ntx = arg->skip; ntx < dev->num_tx_queues; ntx++) { + if (arg->fn(sch, ntx + 1, arg) < 0) { + arg->stop = 1; + break; + } + arg->count++; + } +} + +static const struct Qdisc_class_ops mq_class_ops = { + .select_queue = mq_select_queue, + .graft = mq_graft, + .leaf = mq_leaf, + .get = mq_get, + .put = mq_put, + .walk = mq_walk, + .dump = mq_dump_class, + .dump_stats = mq_dump_class_stats, +}; + +struct Qdisc_ops mq_qdisc_ops __read_mostly = { + .cl_ops = &mq_class_ops, + .id = "mq", + .priv_size = sizeof(struct mq_sched), + .init = mq_init, + .destroy = mq_destroy, + .attach = mq_attach, + .dump = mq_dump, + .owner = THIS_MODULE, +}; -- cgit v1.1 From b275f28535fc774325bb8ad5f664e6c44a8fbc9b Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 1 Sep 2009 08:53:49 -0700 Subject: wireless: update cfg80211 kconfig entry cfg80211 is now *the* wireless configuration API. Lets also give a little explanation as to what it is and refer people to the wireless wiki for more information. Cc: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/Kconfig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 68c504f..abf7ca3 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -1,6 +1,15 @@ config CFG80211 - tristate "Improved wireless configuration API" + tristate "cfg80211 - wireless configuration API" depends on RFKILL || !RFKILL + ---help--- + cfg80211 is the Linux wireless LAN (802.11) configuration API. + Enable this if you have a wireless device. + + For more information refer to documentation on the wireless wiki: + + http://wireless.kernel.org/en/developers/Documentation/cfg80211 + + When built as a module it will be called cfg80211. config NL80211_TESTMODE bool "nl80211 testmode command" -- cgit v1.1 From fa1a9c681377c57e233038e50479e7d7a5cc3108 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 9 Sep 2009 03:43:50 -0700 Subject: headers: net/ipv[46]/protocol.c header trim Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/ipv4/protocol.c | 19 ++----------------- net/ipv6/protocol.c | 15 ++------------- 2 files changed, 4 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index ea50da0..a2e5fc0 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -22,26 +22,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ - -#include -#include +#include #include -#include -#include -#include -#include -#include -#include #include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp; static DEFINE_SPINLOCK(inet_proto_lock); diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index 9ab7891..568864f 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -20,20 +20,9 @@ * - Removed unused variable 'inet6_protocol_base' * - Modified inet6_del_protocol() to correctly maintain copy bit. */ - -#include -#include -#include -#include -#include -#include +#include #include -#include - -#include -#include - -#include +#include #include struct inet6_protocol *inet6_protos[MAX_INET_PROTOS]; -- cgit v1.1 From b2e3abdc708f8c0eff194af25362fdb239abe241 Mon Sep 17 00:00:00 2001 From: Holger Schurig Date: Wed, 9 Sep 2009 13:09:54 +0200 Subject: cfg80211: allow scanning on specified frequencies when using wext-compatibility Handles the case when SIOCSIWSCAN specified iw_scan_req.num_channels and iw_scan_req.channels[]. Signed-off-by: Holger Schurig Signed-off-by: John W. Linville --- net/wireless/scan.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 19c5a9a..4c210c2 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -607,6 +607,9 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; + if (wrqu->data.length == sizeof(struct iw_scan_req)) + wreq = (struct iw_scan_req *)extra; + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) @@ -619,9 +622,14 @@ int cfg80211_wext_siwscan(struct net_device *dev, wiphy = &rdev->wiphy; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; + /* Determine number of channels, needed to allocate creq */ + if (wreq && wreq->num_channels) + n_channels = wreq->num_channels; + else { + 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 *), @@ -638,22 +646,41 @@ int cfg80211_wext_siwscan(struct net_device *dev, creq->n_channels = n_channels; creq->n_ssids = 1; - /* all channels */ + /* translate "Scan on frequencies" request */ 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++) { + + /* If we have a wireless request structure and the + * wireless request specifies frequencies, then search + * for the matching hardware channel. + */ + if (wreq && wreq->num_channels) { + int k; + int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; + for (k = 0; k < wreq->num_channels; k++) { + int wext_freq = wreq->channel_list[k].m / 100000; + if (wext_freq == wiphy_freq) + goto wext_freq_found; + } + goto wext_freq_not_found; + } + + wext_freq_found: creq->channels[i] = &wiphy->bands[band]->channels[j]; i++; + wext_freq_not_found: ; } } - /* translate scan request */ - if (wrqu->data.length == sizeof(struct iw_scan_req)) { - wreq = (struct iw_scan_req *)extra; + /* Set real number of channels specified in creq->channels[] */ + creq->n_channels = i; + /* translate "Scan for SSID" request */ + if (wreq) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) return -EINVAL; -- cgit v1.1 From 23bcf634c8bc0d84607a5b863333191d58baee4c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 9 Sep 2009 18:11:23 -0700 Subject: net_sched: fix estimator lock selection for mq child qdiscs When new child qdiscs are attached to the mq qdisc, they are actually attached as root qdiscs to the device queues. The lock selection for new estimators incorrectly picks the root lock of the existing and to be replaced qdisc, which results in a use-after-free once the old qdisc has been destroyed. Mark mq qdisc instances with a new flag and treat qdiscs attached to mq as children similar to regular root qdiscs. Additionally prevent estimators from being attached to the mq qdisc itself since it only updates its byte and packet counters during dumps. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_api.c | 42 ++++++++++++++++++++++++++---------------- net/sched/sch_mq.c | 1 + 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2a78d54..3af1061 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -733,7 +733,8 @@ static struct lock_class_key qdisc_rx_lock; static struct Qdisc * qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, - u32 parent, u32 handle, struct nlattr **tca, int *errp) + struct Qdisc *p, u32 parent, u32 handle, + struct nlattr **tca, int *errp) { int err; struct nlattr *kind = tca[TCA_KIND]; @@ -810,24 +811,21 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, if (tca[TCA_RATE]) { spinlock_t *root_lock; + err = -EOPNOTSUPP; + if (sch->flags & TCQ_F_MQROOT) + goto err_out4; + if ((sch->parent != TC_H_ROOT) && - !(sch->flags & TCQ_F_INGRESS)) + !(sch->flags & TCQ_F_INGRESS) && + (!p || !(p->flags & TCQ_F_MQROOT))) root_lock = qdisc_root_sleeping_lock(sch); else root_lock = qdisc_lock(sch); err = gen_new_estimator(&sch->bstats, &sch->rate_est, root_lock, tca[TCA_RATE]); - if (err) { - /* - * Any broken qdiscs that would require - * a ops->reset() here? The qdisc was never - * in action so it shouldn't be necessary. - */ - if (ops->destroy) - ops->destroy(sch); - goto err_out3; - } + if (err) + goto err_out4; } qdisc_list_add(sch); @@ -843,6 +841,15 @@ err_out2: err_out: *errp = err; return NULL; + +err_out4: + /* + * Any broken qdiscs that would require a ops->reset() here? + * The qdisc was never in action so it shouldn't be necessary. + */ + if (ops->destroy) + ops->destroy(sch); + goto err_out3; } static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) @@ -867,13 +874,16 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) qdisc_put_stab(sch->stab); sch->stab = stab; - if (tca[TCA_RATE]) + if (tca[TCA_RATE]) { /* NB: ignores errors from replace_estimator because change can't be undone. */ + if (sch->flags & TCQ_F_MQROOT) + goto out; gen_replace_estimator(&sch->bstats, &sch->rate_est, qdisc_root_sleeping_lock(sch), tca[TCA_RATE]); - + } +out: return 0; } @@ -1097,7 +1107,7 @@ create_n_graft: if (!(n->nlmsg_flags&NLM_F_CREATE)) return -ENOENT; if (clid == TC_H_INGRESS) - q = qdisc_create(dev, &dev->rx_queue, + q = qdisc_create(dev, &dev->rx_queue, p, tcm->tcm_parent, tcm->tcm_parent, tca, &err); else { @@ -1106,7 +1116,7 @@ create_n_graft: if (p && p->ops->cl_ops && p->ops->cl_ops->select_queue) ntx = p->ops->cl_ops->select_queue(p, tcm); - q = qdisc_create(dev, netdev_get_tx_queue(dev, ntx), + q = qdisc_create(dev, netdev_get_tx_queue(dev, ntx), p, tcm->tcm_parent, tcm->tcm_handle, tca, &err); } diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index c84dec9..dd5ee02 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -64,6 +64,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt) priv->qdiscs[ntx] = qdisc; } + sch->flags |= TCQ_F_MQROOT; return 0; err: -- cgit v1.1 From 8ba69ba6a324b13e1190fc31e41954d190fd4f1d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 11 Sep 2009 11:31:45 -0700 Subject: net: unix: fix sending fds in multiple buffers Kalle Olavi Niemitalo reported that: "..., when one process calls sendmsg once to send 43804 bytes of data and one file descriptor, and another process then calls recvmsg three times to receive the 16032+16032+11740 bytes, each of those recvmsg calls returns the file descriptor in the ancillary data. I confirmed this with strace. The behaviour differs from Linux 2.6.26, where reportedly only one of those recvmsg calls (I think the first one) returned the file descriptor." This bug was introduced by a patch from me titled "net: unix: fix inflight counting bug in garbage collector", commit 6209344f5. And the reason is, quoting Kalle: "Before your patch, unix_attach_fds() would set scm->fp = NULL, so that if the loop in unix_stream_sendmsg() ran multiple iterations, it could not call unix_attach_fds() again. But now, unix_attach_fds() leaves scm->fp unchanged, and I think this causes it to be called multiple times and duplicate the same file descriptors to each struct sk_buff." Fix this by introducing a flag that is cleared at the start and set when the fds attached to the first buffer. The resulting code should work equivalently to the one on 2.6.26. Reported-by: Kalle Olavi Niemitalo Signed-off-by: Miklos Szeredi Signed-off-by: David S. Miller --- net/unix/af_unix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index fc3ebb9..51ab497 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1501,6 +1501,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, struct sk_buff *skb; int sent = 0; struct scm_cookie tmp_scm; + bool fds_sent = false; if (NULL == siocb->scm) siocb->scm = &tmp_scm; @@ -1562,12 +1563,14 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, size = min_t(int, size, skb_tailroom(skb)); memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); - if (siocb->scm->fp) { + /* Only send the fds in the first buffer */ + if (siocb->scm->fp && !fds_sent) { err = unix_attach_fds(siocb->scm, skb); if (err) { kfree_skb(skb); goto out_err; } + fds_sent = true; } err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); -- cgit v1.1 From ffcfb8db540ff879c2a85bf7e404954281443414 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Sep 2009 11:35:22 -0700 Subject: Subject: [PATCH] appletalk: Fix skb leak when ipddp interface is not loaded And also do a better job of returning proper NET_{RX,XMIT}_ values. Based on a patch and suggestions by Mark Smith. This fixes CVE-2009-2903 Reported-by: Mark Smith Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller --- net/appletalk/aarp.c | 16 +++++++++++----- net/appletalk/ddp.c | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index 89f99d3..9d4adfd 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -599,7 +599,7 @@ int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, /* Non ELAP we cannot do. */ if (dev->type != ARPHRD_ETHER) - return -1; + goto free_it; skb->dev = dev; skb->protocol = htons(ETH_P_ATALK); @@ -634,7 +634,7 @@ int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, if (!a) { /* Whoops slipped... good job it's an unreliable protocol 8) */ write_unlock_bh(&aarp_lock); - return -1; + goto free_it; } /* Set up the queue */ @@ -663,15 +663,21 @@ out_unlock: write_unlock_bh(&aarp_lock); /* Tell the ddp layer we have taken over for this frame. */ - return 0; + goto sent; sendit: if (skb->sk) skb->priority = skb->sk->sk_priority; - dev_queue_xmit(skb); + if (dev_queue_xmit(skb)) + goto drop; sent: - return 1; + return NET_XMIT_SUCCESS; +free_it: + kfree_skb(skb); +drop: + return NET_XMIT_DROP; } +EXPORT_SYMBOL(aarp_send_ddp); /* * An entry in the aarp unresolved queue has become resolved. Send diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0d42d5d..4a6ff2b 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1270,8 +1270,10 @@ static int handle_ip_over_ddp(struct sk_buff *skb) struct net_device_stats *stats; /* This needs to be able to handle ipddp"N" devices */ - if (!dev) - return -ENODEV; + if (!dev) { + kfree_skb(skb); + return NET_RX_DROP; + } skb->protocol = htons(ETH_P_IP); skb_pull(skb, 13); @@ -1281,8 +1283,7 @@ static int handle_ip_over_ddp(struct sk_buff *skb) stats = netdev_priv(dev); stats->rx_packets++; stats->rx_bytes += skb->len + 13; - netif_rx(skb); /* Send the SKB up to a higher place. */ - return 0; + return netif_rx(skb); /* Send the SKB up to a higher place. */ } #else /* make it easy for gcc to optimize this test out, i.e. kill the code */ @@ -1290,9 +1291,8 @@ static int handle_ip_over_ddp(struct sk_buff *skb) #define handle_ip_over_ddp(skb) 0 #endif -static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, - struct ddpehdr *ddp, __u16 len_hops, - int origlen) +static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, + struct ddpehdr *ddp, __u16 len_hops, int origlen) { struct atalk_route *rt; struct atalk_addr ta; @@ -1359,8 +1359,6 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ struct sk_buff *nskb = skb_realloc_headroom(skb, 32); kfree_skb(skb); - if (!nskb) - goto out; skb = nskb; } else skb = skb_unshare(skb, GFP_ATOMIC); @@ -1369,12 +1367,16 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev, * If the buffer didn't vanish into the lack of space bitbucket we can * send it. */ - if (skb && aarp_send_ddp(rt->dev, skb, &ta, NULL) == -1) - goto free_it; -out: - return; + if (skb == NULL) + goto drop; + + if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) + return NET_RX_DROP; + return NET_XMIT_SUCCESS; free_it: kfree_skb(skb); +drop: + return NET_RX_DROP; } /** @@ -1448,8 +1450,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, /* Not ours, so we route the packet via the correct * AppleTalk iface */ - atalk_route_packet(skb, dev, ddp, len_hops, origlen); - return NET_RX_SUCCESS; + return atalk_route_packet(skb, dev, ddp, len_hops, origlen); } /* if IP over DDP is not selected this code will be optimized out */ @@ -1655,10 +1656,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr if (skb2) { loopback = 1; SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); - if (aarp_send_ddp(dev, skb2, - &usat->sat_addr, NULL) == -1) - kfree_skb(skb2); - /* else queued/sent above in the aarp queue */ + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); } } @@ -1688,9 +1689,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr usat = &gsat; } - if (aarp_send_ddp(dev, skb, &usat->sat_addr, NULL) == -1) - kfree_skb(skb); - /* else queued/sent above in the aarp queue */ + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); } SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); @@ -1868,7 +1870,6 @@ static struct packet_type ppptalk_packet_type __read_mostly = { static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; /* Export symbols for use by drivers when AppleTalk is a module */ -EXPORT_SYMBOL(aarp_send_ddp); EXPORT_SYMBOL(atrtr_get_dev); EXPORT_SYMBOL(atalk_find_dev_addr); -- cgit v1.1 From 4fb019a01a7f67342d4a88d26c0817afe392c669 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 11 Sep 2009 11:50:08 -0700 Subject: net: force bridge module(s) to be GPL The only valid usage for the bridge frame hooks are by a GPL components (such as the bridge module). The kernel should not leave a crack in the door for proprietary networking stacks to slip in. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 1a6561b..f843a0c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2116,7 +2116,7 @@ static inline int deliver_skb(struct sk_buff *skb, /* This hook is defined here for ATM LANE */ int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr) __read_mostly; -EXPORT_SYMBOL(br_fdb_test_addr_hook); +EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook); #endif /* @@ -2125,7 +2125,7 @@ EXPORT_SYMBOL(br_fdb_test_addr_hook); */ struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff *skb) __read_mostly; -EXPORT_SYMBOL(br_handle_frame_hook); +EXPORT_SYMBOL_GPL(br_handle_frame_hook); static inline struct sk_buff *handle_bridge(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, -- cgit v1.1 From 384912ed194e43c03ad1cdaa09b0b1e488c34d46 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 31 Aug 2009 21:08:19 +0000 Subject: net: Add DEVTYPE support for Ethernet based devices The Ethernet framing is used for a lot of devices these days. Most prominent are WiFi and WiMAX based devices. However for userspace application it is important to classify these devices correctly and not only see them as Ethernet devices. The daemons like HAL, DeviceKit or even NetworkManager with udev support tries to do the classification in userspace with a lot trickery and extra system calls. This is not good and actually reaches its limitations. Especially since the kernel does know the type of the Ethernet device it is pretty stupid. To solve this problem the underlying device type needs to be set and then the value will be exported as DEVTYPE via uevents and available within udev. # cat /sys/class/net/wlan0/uevent DEVTYPE=wlan INTERFACE=wlan0 IFINDEX=5 This is similar to subsystems like USB and SCSI that distinguish between hosts, devices, disks, partitions etc. The new SET_NETDEV_DEVTYPE() is a convenience helper to set the actual device type. All device types are free form, but for convenience the same strings as used with RFKILL are choosen. Signed-off-by: Marcel Holtmann Signed-off-by: David S. Miller --- net/bluetooth/bnep/core.c | 5 +++++ net/bridge/br_if.c | 6 ++++++ net/mac80211/iface.c | 5 +++++ 3 files changed, 16 insertions(+) (limited to 'net') diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 52a6ce0..cafe9f5 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -533,6 +533,10 @@ static struct device *bnep_get_device(struct bnep_session *session) return conn ? &conn->dev : NULL; } +static struct device_type bnep_type = { + .name = "bluetooth", +}; + int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) { struct net_device *dev; @@ -586,6 +590,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) #endif SET_NETDEV_DEV(dev, bnep_get_device(s)); + SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); if (err) { diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e486f1f..142ebac 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -264,6 +264,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, return p; } +static struct device_type br_type = { + .name = "bridge", +}; + int br_add_bridge(struct net *net, const char *name) { struct net_device *dev; @@ -280,6 +284,8 @@ int br_add_bridge(struct net *net, const char *name) goto out_free; } + SET_NETDEV_DEVTYPE(dev, &br_type); + ret = register_netdevice(dev); if (ret) goto out_free; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f6005ad..b8295cb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -754,6 +754,10 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, return 0; } +static struct device_type wiphy_type = { + .name = "wlan", +}; + int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct net_device **new_dev, enum nl80211_iftype type, struct vif_params *params) @@ -785,6 +789,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); + SET_NETDEV_DEVTYPE(ndev, &wiphy_type); /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ sdata = netdev_priv(ndev); -- cgit v1.1 From cc411d0bae9c19ec85a150aeab4b08335f5751d1 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Wed, 9 Sep 2009 14:41:32 +0000 Subject: ipv6: Add IFA_F_DADFAILED flag Add IFA_F_DADFAILED flag to denote an IPv6 address that has failed Duplicate Address Detection, that way tools like /sbin/ip can be more informative. 3: eth0: mtu 1500 qlen 1000 inet6 2001:db8::1/64 scope global tentative dadfailed valid_lft forever preferred_lft forever Signed-off-by: Brian Haley Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 43b3c9f..c9b3690 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1371,12 +1371,14 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add /* Gets referenced address, destroys ifaddr */ -static void addrconf_dad_stop(struct inet6_ifaddr *ifp) +static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) { if (ifp->flags&IFA_F_PERMANENT) { spin_lock_bh(&ifp->lock); addrconf_del_timer(ifp); ifp->flags |= IFA_F_TENTATIVE; + if (dad_failed) + ifp->flags |= IFA_F_DADFAILED; spin_unlock_bh(&ifp->lock); in6_ifa_put(ifp); #ifdef CONFIG_IPV6_PRIVACY @@ -1422,7 +1424,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) } } - addrconf_dad_stop(ifp); + addrconf_dad_stop(ifp, 1); } /* Join to solicited addr multicast group. */ @@ -2778,7 +2780,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) idev->cnf.accept_dad < 1 || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { - ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); + ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); @@ -2795,7 +2797,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) * - otherwise, kill it. */ in6_ifa_hold(ifp); - addrconf_dad_stop(ifp); + addrconf_dad_stop(ifp, 0); return; } @@ -2829,7 +2831,7 @@ static void addrconf_dad_timer(unsigned long data) * DAD was successful */ - ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); + ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); -- cgit v1.1 From 998ec759ef2fc9c60319815c72b2b699ab939733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 8 Sep 2009 23:59:51 +0000 Subject: Phonet: fix netlink address dump error handling 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 --- net/phonet/pn_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index f8b4cee..d21fd35 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -147,7 +147,7 @@ static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) if (fill_addr(skb, pnd->netdev, addr << 2, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, RTM_NEWADDR)) + cb->nlh->nlmsg_seq, RTM_NEWADDR) < 0) goto out; } } -- cgit v1.1 From f5bb1c558405aaac41b08b2ea71137db9db46e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Wed, 9 Sep 2009 00:00:05 +0000 Subject: Phonet: back-end for autoconfigured addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases, the network device driver knows what layer-3 address the device should have. This adds support for the Phonet stack to automatically request from the driver and add that address to the network device. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- net/phonet/pn_dev.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 5ae4c01..2f65dca 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -195,14 +196,37 @@ found: return err; } +/* automatically configure a Phonet device, if supported */ +static int phonet_device_autoconf(struct net_device *dev) +{ + struct if_phonet_req req; + int ret; + + if (!dev->netdev_ops->ndo_do_ioctl) + return -EOPNOTSUPP; + + ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, + SIOCPNGAUTOCONF); + if (ret < 0) + return ret; + return phonet_address_add(dev, req.ifr_phonet_autoconf.device); +} + /* notify Phonet of device events */ static int phonet_device_notify(struct notifier_block *me, unsigned long what, void *arg) { struct net_device *dev = arg; - if (what == NETDEV_UNREGISTER) + switch (what) { + case NETDEV_REGISTER: + if (dev->type == ARPHRD_PHONET) + phonet_device_autoconf(dev); + break; + case NETDEV_UNREGISTER: phonet_device_destroy(dev); + break; + } return 0; } -- cgit v1.1