inet: Fix missing return value in inet6_hash
[cascardo/linux.git] / net / ipv6 / inet6_hashtables.c
index 70f2628..02761c9 100644 (file)
@@ -69,7 +69,6 @@ struct sock *__inet6_lookup_established(struct net *net,
        struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
 
 
-       rcu_read_lock();
 begin:
        sk_nulls_for_each_rcu(sk, node, &head->chain) {
                if (sk->sk_hash != hash)
@@ -90,7 +89,6 @@ begin:
 out:
        sk = NULL;
 found:
-       rcu_read_unlock();
        return sk;
 }
 EXPORT_SYMBOL(__inet6_lookup_established);
@@ -98,7 +96,7 @@ EXPORT_SYMBOL(__inet6_lookup_established);
 static inline int compute_score(struct sock *sk, struct net *net,
                                const unsigned short hnum,
                                const struct in6_addr *daddr,
-                               const int dif)
+                               const int dif, bool exact_dif)
 {
        int score = -1;
 
@@ -111,7 +109,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score++;
                }
-               if (sk->sk_bound_dev_if) {
+               if (sk->sk_bound_dev_if || exact_dif) {
                        if (sk->sk_bound_dev_if != dif)
                                return -1;
                        score++;
@@ -122,6 +120,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
        return score;
 }
 
+/* called with rcu_read_lock() */
 struct sock *inet6_lookup_listener(struct net *net,
                struct inet_hashinfo *hashinfo,
                struct sk_buff *skb, int doff,
@@ -129,39 +128,28 @@ struct sock *inet6_lookup_listener(struct net *net,
                const __be16 sport, const struct in6_addr *daddr,
                const unsigned short hnum, const int dif)
 {
-       struct sock *sk;
-       const struct hlist_nulls_node *node;
-       struct sock *result;
-       int score, hiscore, matches = 0, reuseport = 0;
-       bool select_ok = true;
-       u32 phash = 0;
        unsigned int hash = inet_lhashfn(net, hnum);
        struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
+       int score, hiscore = 0, matches = 0, reuseport = 0;
+       bool exact_dif = inet6_exact_dif_match(net, skb);
+       struct sock *sk, *result = NULL;
+       u32 phash = 0;
 
-       rcu_read_lock();
-begin:
-       result = NULL;
-       hiscore = 0;
-       sk_nulls_for_each(sk, node, &ilb->head) {
-               score = compute_score(sk, net, hnum, daddr, dif);
+       sk_for_each(sk, &ilb->head) {
+               score = compute_score(sk, net, hnum, daddr, dif, exact_dif);
                if (score > hiscore) {
-                       hiscore = score;
-                       result = sk;
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
                                phash = inet6_ehashfn(net, daddr, hnum,
                                                      saddr, sport);
-                               if (select_ok) {
-                                       struct sock *sk2;
-                                       sk2 = reuseport_select_sock(sk, phash,
-                                                                   skb, doff);
-                                       if (sk2) {
-                                               result = sk2;
-                                               goto found;
-                                       }
-                               }
+                               result = reuseport_select_sock(sk, phash,
+                                                              skb, doff);
+                               if (result)
+                                       return result;
                                matches = 1;
                        }
+                       result = sk;
+                       hiscore = score;
                } else if (score == hiscore && reuseport) {
                        matches++;
                        if (reciprocal_scale(phash, matches) == 0)
@@ -169,25 +157,6 @@ begin:
                        phash = next_pseudo_random32(phash);
                }
        }
-       /*
-        * if the nulls value we got at the end of this lookup is
-        * not the expected one, we must restart lookup.
-        * We probably met an item that was moved to another chain.
-        */
-       if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE)
-               goto begin;
-       if (result) {
-found:
-               if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
-                       result = NULL;
-               else if (unlikely(compute_score(result, net, hnum, daddr,
-                                 dif) < hiscore)) {
-                       sock_put(result);
-                       select_ok = false;
-                       goto begin;
-               }
-       }
-       rcu_read_unlock();
        return result;
 }
 EXPORT_SYMBOL_GPL(inet6_lookup_listener);
@@ -199,12 +168,12 @@ struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
                          const int dif)
 {
        struct sock *sk;
+       bool refcounted;
 
-       local_bh_disable();
        sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr,
-                           ntohs(dport), dif);
-       local_bh_enable();
-
+                           ntohs(dport), dif, &refcounted);
+       if (sk && !refcounted && !atomic_inc_not_zero(&sk->sk_refcnt))
+               sk = NULL;
        return sk;
 }
 EXPORT_SYMBOL_GPL(inet6_lookup);
@@ -254,7 +223,7 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
        __sk_nulls_add_node_rcu(sk, &head->chain);
        if (tw) {
                sk_nulls_del_node_init_rcu((struct sock *)tw);
-               NET_INC_STATS_BH(net, LINUX_MIB_TIMEWAITRECYCLED);
+               __NET_INC_STATS(net, LINUX_MIB_TIMEWAITRECYCLED);
        }
        spin_unlock(lock);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
@@ -295,13 +264,15 @@ EXPORT_SYMBOL_GPL(inet6_hash_connect);
 
 int inet6_hash(struct sock *sk)
 {
+       int err = 0;
+
        if (sk->sk_state != TCP_CLOSE) {
                local_bh_disable();
-               __inet_hash(sk, NULL, ipv6_rcv_saddr_equal);
+               err = __inet_hash(sk, NULL, ipv6_rcv_saddr_equal);
                local_bh_enable();
        }
 
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL_GPL(inet6_hash);