wl1251: fix a memory leak in probe
[safe/jmp/linux-2.6] / net / llc / llc_sap.c
index 008de1f..94e7fca 100644 (file)
@@ -23,6 +23,7 @@
 #include <net/sock.h>
 #include <net/tcp_states.h>
 #include <linux/llc.h>
+#include <linux/slab.h>
 
 static int llc_mac_header_len(unsigned short devtype)
 {
@@ -30,7 +31,7 @@ static int llc_mac_header_len(unsigned short devtype)
        case ARPHRD_ETHER:
        case ARPHRD_LOOPBACK:
                return sizeof(struct ethhdr);
-#ifdef CONFIG_TR
+#if defined(CONFIG_TR) || defined(CONFIG_TR_MODULE)
        case ARPHRD_IEEE802_TR:
                return sizeof(struct trh_hdr);
 #endif
@@ -297,6 +298,17 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb,
        llc_sap_state_process(sap, skb);
 }
 
+static inline bool llc_dgram_match(const struct llc_sap *sap,
+                                  const struct llc_addr *laddr,
+                                  const struct sock *sk)
+{
+     struct llc_sock *llc = llc_sk(sk);
+
+     return sk->sk_type == SOCK_DGRAM &&
+         llc->laddr.lsap == laddr->lsap &&
+         llc_mac_match(llc->laddr.mac, laddr->mac);
+}
+
 /**
  *     llc_lookup_dgram - Finds dgram socket for the local sap/mac
  *     @sap: SAP
@@ -309,25 +321,68 @@ static struct sock *llc_lookup_dgram(struct llc_sap *sap,
                                     const struct llc_addr *laddr)
 {
        struct sock *rc;
-       struct hlist_node *node;
-
-       read_lock_bh(&sap->sk_list.lock);
-       sk_for_each(rc, node, &sap->sk_list.list) {
-               struct llc_sock *llc = llc_sk(rc);
-
-               if (rc->sk_type == SOCK_DGRAM &&
-                   llc->laddr.lsap == laddr->lsap &&
-                   llc_mac_match(llc->laddr.mac, laddr->mac)) {
-                       sock_hold(rc);
+       struct hlist_nulls_node *node;
+       int slot = llc_sk_laddr_hashfn(sap, laddr);
+       struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
+
+       rcu_read_lock_bh();
+again:
+       sk_nulls_for_each_rcu(rc, node, laddr_hb) {
+               if (llc_dgram_match(sap, laddr, rc)) {
+                       /* Extra checks required by SLAB_DESTROY_BY_RCU */
+                       if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
+                               goto again;
+                       if (unlikely(llc_sk(rc)->sap != sap ||
+                                    !llc_dgram_match(sap, laddr, rc))) {
+                               sock_put(rc);
+                               continue;
+                       }
                        goto found;
                }
        }
        rc = NULL;
+       /*
+        * if the nulls value we got at the end of this lookup is
+        * not the expected one, we must restart lookup.
+        * We probably met an item that was moved to another chain.
+        */
+       if (unlikely(get_nulls_value(node) != slot))
+               goto again;
 found:
-       read_unlock_bh(&sap->sk_list.lock);
+       rcu_read_unlock_bh();
        return rc;
 }
 
+static inline bool llc_mcast_match(const struct llc_sap *sap,
+                                  const struct llc_addr *laddr,
+                                  const struct sk_buff *skb,
+                                  const struct sock *sk)
+{
+     struct llc_sock *llc = llc_sk(sk);
+
+     return sk->sk_type == SOCK_DGRAM &&
+         llc->laddr.lsap == laddr->lsap &&
+         llc->dev == skb->dev;
+}
+
+static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb,
+                        struct sock **stack, int count)
+{
+       struct sk_buff *skb1;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               skb1 = skb_clone(skb, GFP_ATOMIC);
+               if (!skb1) {
+                       sock_put(stack[i]);
+                       continue;
+               }
+
+               llc_sap_rcv(sap, skb1, stack[i]);
+               sock_put(stack[i]);
+       }
+}
+
 /**
  *     llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets.
  *     @sap: SAP
@@ -340,32 +395,31 @@ static void llc_sap_mcast(struct llc_sap *sap,
                          const struct llc_addr *laddr,
                          struct sk_buff *skb)
 {
-       struct sock *sk;
+       int i = 0, count = 256 / sizeof(struct sock *);
+       struct sock *sk, *stack[count];
        struct hlist_node *node;
+       struct llc_sock *llc;
+       struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex);
 
-       read_lock_bh(&sap->sk_list.lock);
-       sk_for_each(sk, node, &sap->sk_list.list) {
-               struct llc_sock *llc = llc_sk(sk);
-               struct sk_buff *skb1;
+       spin_lock_bh(&sap->sk_lock);
+       hlist_for_each_entry(llc, node, dev_hb, dev_hash_node) {
 
-               if (sk->sk_type != SOCK_DGRAM)
-                       continue;
+               sk = &llc->sk;
 
-               if (llc->laddr.lsap != laddr->lsap)
+               if (!llc_mcast_match(sap, laddr, skb, sk))
                        continue;
 
-               if (llc->dev != skb->dev)
-                       continue;
-
-               skb1 = skb_clone(skb, GFP_ATOMIC);
-               if (!skb1)
-                       break;
-
                sock_hold(sk);
-               llc_sap_rcv(sap, skb1, sk);
-               sock_put(sk);
+               if (i < count)
+                       stack[i++] = sk;
+               else {
+                       llc_do_mcast(sap, skb, stack, i);
+                       i = 0;
+               }
        }
-       read_unlock_bh(&sap->sk_list.lock);
+       spin_unlock_bh(&sap->sk_lock);
+
+       llc_do_mcast(sap, skb, stack, i);
 }