|
@@ -198,13 +198,13 @@ static inline int compute_score(struct sock *sk, struct net *net,
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Don't inline this cruft. Here are some nice properties to exploit here. The
|
|
|
- * BSD API does not allow a listening sock to specify the remote port nor the
|
|
|
+ * Here are some nice properties to exploit here. The BSD API
|
|
|
+ * does not allow a listening sock to specify the remote port nor the
|
|
|
* remote address for the connection. So always assume those are both
|
|
|
* wildcarded during the search since they can never be otherwise.
|
|
|
*/
|
|
|
|
|
|
-
|
|
|
+/* called with rcu_read_lock() : No refcount taken on the socket */
|
|
|
struct sock *__inet_lookup_listener(struct net *net,
|
|
|
struct inet_hashinfo *hashinfo,
|
|
|
struct sk_buff *skb, int doff,
|
|
@@ -212,37 +212,27 @@ struct sock *__inet_lookup_listener(struct net *net,
|
|
|
const __be32 daddr, const unsigned short hnum,
|
|
|
const int dif)
|
|
|
{
|
|
|
- struct sock *sk, *result;
|
|
|
- struct hlist_nulls_node *node;
|
|
|
unsigned int hash = inet_lhashfn(net, hnum);
|
|
|
struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
|
|
|
- int score, hiscore, matches = 0, reuseport = 0;
|
|
|
- bool select_ok = true;
|
|
|
+ int score, hiscore = 0, matches = 0, reuseport = 0;
|
|
|
+ struct sock *sk, *result = NULL;
|
|
|
u32 phash = 0;
|
|
|
|
|
|
-begin:
|
|
|
- result = NULL;
|
|
|
- hiscore = 0;
|
|
|
- sk_nulls_for_each_rcu(sk, node, &ilb->head) {
|
|
|
+ sk_for_each_rcu(sk, &ilb->head) {
|
|
|
score = compute_score(sk, net, hnum, daddr, dif);
|
|
|
if (score > hiscore) {
|
|
|
- result = sk;
|
|
|
- hiscore = score;
|
|
|
reuseport = sk->sk_reuseport;
|
|
|
if (reuseport) {
|
|
|
phash = inet_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)
|
|
@@ -250,24 +240,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;
|
|
|
- }
|
|
|
- }
|
|
|
return result;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__inet_lookup_listener);
|
|
@@ -508,7 +480,8 @@ int __inet_hash(struct sock *sk, struct sock *osk,
|
|
|
if (err)
|
|
|
goto unlock;
|
|
|
}
|
|
|
- __sk_nulls_add_node_rcu(sk, &ilb->head);
|
|
|
+ hlist_add_head_rcu(&sk->sk_node, &ilb->head);
|
|
|
+ sock_set_flag(sk, SOCK_RCU_FREE);
|
|
|
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
|
|
|
unlock:
|
|
|
spin_unlock(&ilb->lock);
|
|
@@ -535,20 +508,25 @@ void inet_unhash(struct sock *sk)
|
|
|
{
|
|
|
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
|
|
|
spinlock_t *lock;
|
|
|
+ bool listener = false;
|
|
|
int done;
|
|
|
|
|
|
if (sk_unhashed(sk))
|
|
|
return;
|
|
|
|
|
|
- if (sk->sk_state == TCP_LISTEN)
|
|
|
+ if (sk->sk_state == TCP_LISTEN) {
|
|
|
lock = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)].lock;
|
|
|
- else
|
|
|
+ listener = true;
|
|
|
+ } else {
|
|
|
lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
|
|
|
-
|
|
|
+ }
|
|
|
spin_lock_bh(lock);
|
|
|
if (rcu_access_pointer(sk->sk_reuseport_cb))
|
|
|
reuseport_detach_sock(sk);
|
|
|
- done = __sk_nulls_del_node_init_rcu(sk);
|
|
|
+ if (listener)
|
|
|
+ done = __sk_del_node_init(sk);
|
|
|
+ else
|
|
|
+ done = __sk_nulls_del_node_init_rcu(sk);
|
|
|
if (done)
|
|
|
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
|
|
|
spin_unlock_bh(lock);
|
|
@@ -684,9 +662,8 @@ void inet_hashinfo_init(struct inet_hashinfo *h)
|
|
|
|
|
|
for (i = 0; i < INET_LHTABLE_SIZE; i++) {
|
|
|
spin_lock_init(&h->listening_hash[i].lock);
|
|
|
- INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head,
|
|
|
- i + LISTENING_NULLS_BASE);
|
|
|
- }
|
|
|
+ INIT_HLIST_HEAD(&h->listening_hash[i].head);
|
|
|
+ }
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(inet_hashinfo_init);
|
|
|
|