Merge tag 'ktest-v3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux...
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / inet6_hashtables.c
index dea17fd28e5037295d56cf517671b479a1bca825..32b4a1675d826d8ce50e36dbf314456075e1660e 100644 (file)
@@ -158,25 +158,38 @@ static inline int compute_score(struct sock *sk, struct net *net,
 }
 
 struct sock *inet6_lookup_listener(struct net *net,
-               struct inet_hashinfo *hashinfo, const struct in6_addr *daddr,
+               struct inet_hashinfo *hashinfo, const struct in6_addr *saddr,
+               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;
+       int score, hiscore, matches = 0, reuseport = 0;
+       u32 phash = 0;
        unsigned int hash = inet_lhashfn(net, hnum);
        struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
 
        rcu_read_lock();
 begin:
        result = NULL;
-       hiscore = -1;
+       hiscore = 0;
        sk_nulls_for_each(sk, node, &ilb->head) {
                score = compute_score(sk, net, hnum, daddr, dif);
                if (score > hiscore) {
                        hiscore = score;
                        result = sk;
+                       reuseport = sk->sk_reuseport;
+                       if (reuseport) {
+                               phash = inet6_ehashfn(net, daddr, hnum,
+                                                     saddr, sport);
+                               matches = 1;
+                       }
+               } else if (score == hiscore && reuseport) {
+                       matches++;
+                       if (((u64)phash * matches) >> 32 == 0)
+                               result = sk;
+                       phash = next_pseudo_random32(phash);
                }
        }
        /*