2005-08-12 20:26:18 +08:00
|
|
|
/*
|
|
|
|
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
|
|
|
* operating system. INET is implemented using the BSD Socket
|
|
|
|
* interface as the means of communication with the user level.
|
|
|
|
*
|
|
|
|
* Generic INET6 transport hashtables
|
|
|
|
*
|
2005-12-14 15:25:44 +08:00
|
|
|
* Authors: Lotsa people, from code originally in tcp, generalised here
|
2014-08-25 04:53:10 +08:00
|
|
|
* by Arnaldo Carvalho de Melo <acme@mandriva.com>
|
2005-08-12 20:26:18 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
2005-12-14 15:25:44 +08:00
|
|
|
#include <linux/random.h>
|
2005-08-12 20:26:18 +08:00
|
|
|
|
2016-02-11 00:50:40 +08:00
|
|
|
#include <net/addrconf.h>
|
2005-08-12 20:26:18 +08:00
|
|
|
#include <net/inet_connection_sock.h>
|
|
|
|
#include <net/inet_hashtables.h>
|
|
|
|
#include <net/inet6_hashtables.h>
|
2011-08-04 11:50:44 +08:00
|
|
|
#include <net/secure_seq.h>
|
2005-12-14 15:25:44 +08:00
|
|
|
#include <net/ip.h>
|
2016-02-11 00:50:40 +08:00
|
|
|
#include <net/sock_reuseport.h>
|
2005-08-12 20:26:18 +08:00
|
|
|
|
2015-03-19 05:05:35 +08:00
|
|
|
u32 inet6_ehashfn(const struct net *net,
|
|
|
|
const struct in6_addr *laddr, const u16 lport,
|
|
|
|
const struct in6_addr *faddr, const __be16 fport)
|
2013-10-20 03:48:52 +08:00
|
|
|
{
|
2013-10-20 03:48:57 +08:00
|
|
|
static u32 inet6_ehash_secret __read_mostly;
|
|
|
|
static u32 ipv6_hash_secret __read_mostly;
|
|
|
|
|
|
|
|
u32 lhash, fhash;
|
|
|
|
|
|
|
|
net_get_random_once(&inet6_ehash_secret, sizeof(inet6_ehash_secret));
|
|
|
|
net_get_random_once(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
|
|
|
|
|
|
|
|
lhash = (__force u32)laddr->s6_addr32[3];
|
|
|
|
fhash = __ipv6_addr_jhash(faddr, ipv6_hash_secret);
|
|
|
|
|
2013-10-20 03:48:52 +08:00
|
|
|
return __inet6_ehashfn(lhash, lport, fhash, fport,
|
2013-10-20 03:48:57 +08:00
|
|
|
inet6_ehash_secret + net_hash_mix(net));
|
2013-10-20 03:48:52 +08:00
|
|
|
}
|
|
|
|
|
2006-04-10 13:48:59 +08:00
|
|
|
/*
|
|
|
|
* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
|
|
|
|
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
|
|
|
|
*
|
|
|
|
* The sockhash lock must be held as a reader here.
|
|
|
|
*/
|
2008-01-31 21:07:21 +08:00
|
|
|
struct sock *__inet6_lookup_established(struct net *net,
|
|
|
|
struct inet_hashinfo *hashinfo,
|
2006-04-10 13:48:59 +08:00
|
|
|
const struct in6_addr *saddr,
|
2006-11-08 16:20:00 +08:00
|
|
|
const __be16 sport,
|
2006-04-10 13:48:59 +08:00
|
|
|
const struct in6_addr *daddr,
|
|
|
|
const u16 hnum,
|
2017-08-07 23:44:21 +08:00
|
|
|
const int dif, const int sdif)
|
2006-04-10 13:48:59 +08:00
|
|
|
{
|
|
|
|
struct sock *sk;
|
2008-11-17 11:40:17 +08:00
|
|
|
const struct hlist_nulls_node *node;
|
2006-09-28 09:43:07 +08:00
|
|
|
const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
|
2006-04-10 13:48:59 +08:00
|
|
|
/* Optimize here for direct hit, only listening connections can
|
|
|
|
* have wildcards anyways.
|
|
|
|
*/
|
2008-06-17 08:13:48 +08:00
|
|
|
unsigned int hash = inet6_ehashfn(net, daddr, hnum, saddr, sport);
|
2009-10-09 08:16:19 +08:00
|
|
|
unsigned int slot = hash & hashinfo->ehash_mask;
|
2008-11-17 11:40:17 +08:00
|
|
|
struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
|
2006-04-10 13:48:59 +08:00
|
|
|
|
2008-11-17 11:40:17 +08:00
|
|
|
|
|
|
|
begin:
|
|
|
|
sk_nulls_for_each_rcu(sk, node, &head->chain) {
|
2012-11-30 17:49:27 +08:00
|
|
|
if (sk->sk_hash != hash)
|
|
|
|
continue;
|
2017-08-07 23:44:21 +08:00
|
|
|
if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif))
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
continue;
|
2017-06-30 18:08:01 +08:00
|
|
|
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
goto out;
|
|
|
|
|
2017-08-07 23:44:21 +08:00
|
|
|
if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif))) {
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
sock_gen_put(sk);
|
|
|
|
goto begin;
|
2008-11-17 11:40:17 +08:00
|
|
|
}
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
goto found;
|
2006-04-10 13:48:59 +08:00
|
|
|
}
|
2008-11-17 11:40:17 +08:00
|
|
|
if (get_nulls_value(node) != slot)
|
|
|
|
goto begin;
|
|
|
|
out:
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
sk = NULL;
|
|
|
|
found:
|
2006-04-10 13:48:59 +08:00
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__inet6_lookup_established);
|
|
|
|
|
2011-01-17 07:09:38 +08:00
|
|
|
static inline int compute_score(struct sock *sk, struct net *net,
|
2008-11-24 09:22:55 +08:00
|
|
|
const unsigned short hnum,
|
|
|
|
const struct in6_addr *daddr,
|
2017-08-07 23:44:21 +08:00
|
|
|
const int dif, const int sdif, bool exact_dif)
|
2008-11-24 09:22:55 +08:00
|
|
|
{
|
|
|
|
int score = -1;
|
2018-11-07 23:36:03 +08:00
|
|
|
bool dev_match;
|
2008-11-24 09:22:55 +08:00
|
|
|
|
2009-10-15 14:30:45 +08:00
|
|
|
if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum &&
|
2008-11-24 09:22:55 +08:00
|
|
|
sk->sk_family == PF_INET6) {
|
|
|
|
|
|
|
|
score = 1;
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
|
|
|
|
if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
|
2008-11-24 09:22:55 +08:00
|
|
|
return -1;
|
|
|
|
score++;
|
|
|
|
}
|
2018-11-07 23:36:03 +08:00
|
|
|
dev_match = inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
|
|
|
|
dif, sdif);
|
|
|
|
if (!dev_match)
|
|
|
|
return -1;
|
|
|
|
score++;
|
2017-08-07 23:44:21 +08:00
|
|
|
|
2015-10-09 10:33:21 +08:00
|
|
|
if (sk->sk_incoming_cpu == raw_smp_processor_id())
|
|
|
|
score++;
|
2008-11-24 09:22:55 +08:00
|
|
|
}
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
2016-04-01 23:52:17 +08:00
|
|
|
/* called with rcu_read_lock() */
|
2017-12-02 04:52:31 +08:00
|
|
|
static struct sock *inet6_lhash2_lookup(struct net *net,
|
|
|
|
struct inet_listen_hashbucket *ilb2,
|
|
|
|
struct sk_buff *skb, int doff,
|
|
|
|
const struct in6_addr *saddr,
|
|
|
|
const __be16 sport, const struct in6_addr *daddr,
|
|
|
|
const unsigned short hnum, const int dif, const int sdif)
|
|
|
|
{
|
|
|
|
bool exact_dif = inet6_exact_dif_match(net, skb);
|
|
|
|
struct inet_connection_sock *icsk;
|
|
|
|
struct sock *sk, *result = NULL;
|
|
|
|
int score, hiscore = 0;
|
|
|
|
u32 phash = 0;
|
|
|
|
|
|
|
|
inet_lhash2_for_each_icsk_rcu(icsk, &ilb2->head) {
|
|
|
|
sk = (struct sock *)icsk;
|
|
|
|
score = compute_score(sk, net, hnum, daddr, dif, sdif,
|
|
|
|
exact_dif);
|
|
|
|
if (score > hiscore) {
|
|
|
|
if (sk->sk_reuseport) {
|
|
|
|
phash = inet6_ehashfn(net, daddr, hnum,
|
|
|
|
saddr, sport);
|
|
|
|
result = reuseport_select_sock(sk, phash,
|
|
|
|
skb, doff);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result = sk;
|
|
|
|
hiscore = score;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-01-31 21:07:21 +08:00
|
|
|
struct sock *inet6_lookup_listener(struct net *net,
|
2016-02-11 00:50:38 +08:00
|
|
|
struct inet_hashinfo *hashinfo,
|
|
|
|
struct sk_buff *skb, int doff,
|
|
|
|
const struct in6_addr *saddr,
|
2013-01-22 17:50:39 +08:00
|
|
|
const __be16 sport, const struct in6_addr *daddr,
|
2017-08-07 23:44:21 +08:00
|
|
|
const unsigned short hnum, const int dif, const int sdif)
|
2005-08-12 20:26:18 +08:00
|
|
|
{
|
2008-11-24 09:22:55 +08:00
|
|
|
unsigned int hash = inet_lhashfn(net, hnum);
|
|
|
|
struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
|
2016-10-17 11:02:52 +08:00
|
|
|
bool exact_dif = inet6_exact_dif_match(net, skb);
|
2017-12-02 04:52:31 +08:00
|
|
|
struct inet_listen_hashbucket *ilb2;
|
2016-04-01 23:52:17 +08:00
|
|
|
struct sock *sk, *result = NULL;
|
2017-11-30 22:39:34 +08:00
|
|
|
int score, hiscore = 0;
|
2017-12-02 04:52:31 +08:00
|
|
|
unsigned int hash2;
|
2016-04-01 23:52:17 +08:00
|
|
|
u32 phash = 0;
|
2008-11-24 09:22:55 +08:00
|
|
|
|
2017-12-02 04:52:31 +08:00
|
|
|
if (ilb->count <= 10 || !hashinfo->lhash2)
|
|
|
|
goto port_lookup;
|
|
|
|
|
|
|
|
/* Too many sk in the ilb bucket (which is hashed by port alone).
|
|
|
|
* Try lhash2 (which is hashed by port and addr) instead.
|
|
|
|
*/
|
|
|
|
|
|
|
|
hash2 = ipv6_portaddr_hash(net, daddr, hnum);
|
|
|
|
ilb2 = inet_lhash2_bucket(hashinfo, hash2);
|
|
|
|
if (ilb2->count > ilb->count)
|
|
|
|
goto port_lookup;
|
|
|
|
|
|
|
|
result = inet6_lhash2_lookup(net, ilb2, skb, doff,
|
|
|
|
saddr, sport, daddr, hnum,
|
|
|
|
dif, sdif);
|
|
|
|
if (result)
|
2018-08-08 16:01:26 +08:00
|
|
|
goto done;
|
2017-12-02 04:52:31 +08:00
|
|
|
|
|
|
|
/* Lookup lhash2 with in6addr_any */
|
|
|
|
|
|
|
|
hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum);
|
|
|
|
ilb2 = inet_lhash2_bucket(hashinfo, hash2);
|
|
|
|
if (ilb2->count > ilb->count)
|
|
|
|
goto port_lookup;
|
|
|
|
|
2018-08-08 16:01:26 +08:00
|
|
|
result = inet6_lhash2_lookup(net, ilb2, skb, doff,
|
|
|
|
saddr, sport, daddr, hnum,
|
|
|
|
dif, sdif);
|
|
|
|
goto done;
|
2017-12-02 04:52:31 +08:00
|
|
|
|
|
|
|
port_lookup:
|
2016-04-01 23:52:17 +08:00
|
|
|
sk_for_each(sk, &ilb->head) {
|
2017-08-07 23:44:21 +08:00
|
|
|
score = compute_score(sk, net, hnum, daddr, dif, sdif, exact_dif);
|
2008-11-24 09:22:55 +08:00
|
|
|
if (score > hiscore) {
|
2017-11-30 22:39:34 +08:00
|
|
|
if (sk->sk_reuseport) {
|
2013-01-22 17:50:39 +08:00
|
|
|
phash = inet6_ehashfn(net, daddr, hnum,
|
|
|
|
saddr, sport);
|
2016-04-01 23:52:17 +08:00
|
|
|
result = reuseport_select_sock(sk, phash,
|
|
|
|
skb, doff);
|
|
|
|
if (result)
|
2018-08-08 16:01:26 +08:00
|
|
|
goto done;
|
2013-01-22 17:50:39 +08:00
|
|
|
}
|
2016-04-01 23:52:17 +08:00
|
|
|
result = sk;
|
2016-04-09 23:01:13 +08:00
|
|
|
hiscore = score;
|
2005-08-12 20:26:18 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-08 16:01:26 +08:00
|
|
|
done:
|
|
|
|
if (unlikely(IS_ERR(result)))
|
|
|
|
return NULL;
|
2005-08-12 20:26:18 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(inet6_lookup_listener);
|
|
|
|
|
2008-01-31 21:07:21 +08:00
|
|
|
struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
|
2016-02-11 00:50:38 +08:00
|
|
|
struct sk_buff *skb, int doff,
|
2006-11-08 16:20:00 +08:00
|
|
|
const struct in6_addr *saddr, const __be16 sport,
|
|
|
|
const struct in6_addr *daddr, const __be16 dport,
|
2005-08-12 20:26:18 +08:00
|
|
|
const int dif)
|
|
|
|
{
|
|
|
|
struct sock *sk;
|
2016-04-01 23:52:17 +08:00
|
|
|
bool refcounted;
|
2005-08-12 20:26:18 +08:00
|
|
|
|
2016-02-11 00:50:38 +08:00
|
|
|
sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr,
|
2017-08-07 23:44:21 +08:00
|
|
|
ntohs(dport), dif, 0, &refcounted);
|
2017-06-30 18:08:01 +08:00
|
|
|
if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt))
|
2016-04-01 23:52:17 +08:00
|
|
|
sk = NULL;
|
2005-08-12 20:26:18 +08:00
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(inet6_lookup);
|
2005-12-14 15:25:44 +08:00
|
|
|
|
|
|
|
static int __inet6_check_established(struct inet_timewait_death_row *death_row,
|
|
|
|
struct sock *sk, const __u16 lport,
|
|
|
|
struct inet_timewait_sock **twp)
|
|
|
|
{
|
|
|
|
struct inet_hashinfo *hinfo = death_row->hashinfo;
|
2006-03-14 06:26:12 +08:00
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
const struct in6_addr *daddr = &sk->sk_v6_rcv_saddr;
|
|
|
|
const struct in6_addr *saddr = &sk->sk_v6_daddr;
|
2005-12-14 15:25:44 +08:00
|
|
|
const int dif = sk->sk_bound_dev_if;
|
2008-06-17 08:13:48 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2017-08-07 23:44:21 +08:00
|
|
|
const int sdif = l3mdev_master_ifindex_by_index(net, dif);
|
|
|
|
const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
|
2008-06-17 08:13:48 +08:00
|
|
|
const unsigned int hash = inet6_ehashfn(net, daddr, lport, saddr,
|
2009-10-15 14:30:45 +08:00
|
|
|
inet->inet_dport);
|
2005-12-14 15:25:44 +08:00
|
|
|
struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
|
2008-11-21 12:39:09 +08:00
|
|
|
spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
|
2005-12-14 15:25:44 +08:00
|
|
|
struct sock *sk2;
|
2008-11-17 11:40:17 +08:00
|
|
|
const struct hlist_nulls_node *node;
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
struct inet_timewait_sock *tw = NULL;
|
2005-12-14 15:25:44 +08:00
|
|
|
|
2008-11-21 12:39:09 +08:00
|
|
|
spin_lock(lock);
|
2005-12-14 15:25:44 +08:00
|
|
|
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
sk_nulls_for_each(sk2, node, &head->chain) {
|
2012-11-30 17:49:27 +08:00
|
|
|
if (sk2->sk_hash != hash)
|
|
|
|
continue;
|
2005-12-14 15:25:44 +08:00
|
|
|
|
2017-08-07 23:44:21 +08:00
|
|
|
if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports,
|
|
|
|
dif, sdif))) {
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
if (sk2->sk_state == TCP_TIME_WAIT) {
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
tw = inet_twsk(sk2);
|
|
|
|
if (twsk_unique(sk, sk2, twp))
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
break;
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 15:22:02 +08:00
|
|
|
}
|
2005-12-14 15:25:44 +08:00
|
|
|
goto not_unique;
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
}
|
2005-12-14 15:25:44 +08:00
|
|
|
}
|
|
|
|
|
2006-03-14 06:26:12 +08:00
|
|
|
/* Must record num and sport now. Otherwise we will see
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
* in hash table socket with a funny identity.
|
|
|
|
*/
|
2009-10-15 14:30:45 +08:00
|
|
|
inet->inet_num = lport;
|
|
|
|
inet->inet_sport = htons(lport);
|
2009-12-03 06:31:19 +08:00
|
|
|
sk->sk_hash = hash;
|
2008-07-26 12:43:18 +08:00
|
|
|
WARN_ON(!sk_unhashed(sk));
|
2008-11-17 11:40:17 +08:00
|
|
|
__sk_nulls_add_node_rcu(sk, &head->chain);
|
2009-12-03 06:31:19 +08:00
|
|
|
if (tw) {
|
2015-07-09 05:28:29 +08:00
|
|
|
sk_nulls_del_node_init_rcu((struct sock *)tw);
|
2016-04-28 07:44:39 +08:00
|
|
|
__NET_INC_STATS(net, LINUX_MIB_TIMEWAITRECYCLED);
|
2009-12-03 06:31:19 +08:00
|
|
|
}
|
2008-11-21 12:39:09 +08:00
|
|
|
spin_unlock(lock);
|
2008-04-01 10:41:46 +08:00
|
|
|
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
|
2005-12-14 15:25:44 +08:00
|
|
|
|
2009-12-03 06:31:19 +08:00
|
|
|
if (twp) {
|
2005-12-14 15:25:44 +08:00
|
|
|
*twp = tw;
|
2009-12-03 06:31:19 +08:00
|
|
|
} else if (tw) {
|
2005-12-14 15:25:44 +08:00
|
|
|
/* Silly. Should hash-dance instead... */
|
2015-07-09 05:28:30 +08:00
|
|
|
inet_twsk_deschedule_put(tw);
|
2005-12-14 15:25:44 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
not_unique:
|
2008-11-21 12:39:09 +08:00
|
|
|
spin_unlock(lock);
|
2005-12-14 15:25:44 +08:00
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
2015-05-28 01:46:02 +08:00
|
|
|
static u32 inet6_sk_port_offset(const struct sock *sk)
|
2005-12-14 15:25:44 +08:00
|
|
|
{
|
|
|
|
const struct inet_sock *inet = inet_sk(sk);
|
ipv6: make lookups simpler and faster
TCP listener refactoring, part 4 :
To speed up inet lookups, we moved IPv4 addresses from inet to struct
sock_common
Now is time to do the same for IPv6, because it permits us to have fast
lookups for all kind of sockets, including upcoming SYN_RECV.
Getting IPv6 addresses in TCP lookups currently requires two extra cache
lines, plus a dereference (and memory stall).
inet6_sk(sk) does the dereference of inet_sk(__sk)->pinet6
This patch is way bigger than its IPv4 counter part, because for IPv4,
we could add aliases (inet_daddr, inet_rcv_saddr), while on IPv6,
it's not doable easily.
inet6_sk(sk)->daddr becomes sk->sk_v6_daddr
inet6_sk(sk)->rcv_saddr becomes sk->sk_v6_rcv_saddr
And timewait socket also have tw->tw_v6_daddr & tw->tw_v6_rcv_saddr
at the same offset.
We get rid of INET6_TW_MATCH() as INET6_MATCH() is now the generic
macro.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-04 06:42:29 +08:00
|
|
|
|
|
|
|
return secure_ipv6_port_ephemeral(sk->sk_v6_rcv_saddr.s6_addr32,
|
|
|
|
sk->sk_v6_daddr.s6_addr32,
|
2009-10-15 14:30:45 +08:00
|
|
|
inet->inet_dport);
|
2005-12-14 15:25:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int inet6_hash_connect(struct inet_timewait_death_row *death_row,
|
|
|
|
struct sock *sk)
|
|
|
|
{
|
2015-05-28 01:46:02 +08:00
|
|
|
u32 port_offset = 0;
|
|
|
|
|
|
|
|
if (!inet_sk(sk)->inet_num)
|
|
|
|
port_offset = inet6_sk_port_offset(sk);
|
|
|
|
return __inet_hash_connect(death_row, sk, port_offset,
|
2015-03-19 05:05:37 +08:00
|
|
|
__inet6_check_established);
|
2005-12-14 15:25:44 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(inet6_hash_connect);
|
2016-02-11 00:50:36 +08:00
|
|
|
|
|
|
|
int inet6_hash(struct sock *sk)
|
|
|
|
{
|
2016-10-26 06:08:49 +08:00
|
|
|
int err = 0;
|
|
|
|
|
2016-02-11 00:50:36 +08:00
|
|
|
if (sk->sk_state != TCP_CLOSE) {
|
|
|
|
local_bh_disable();
|
2017-01-17 23:51:01 +08:00
|
|
|
err = __inet_hash(sk, NULL);
|
2016-02-11 00:50:36 +08:00
|
|
|
local_bh_enable();
|
|
|
|
}
|
|
|
|
|
2016-10-26 06:08:49 +08:00
|
|
|
return err;
|
2016-02-11 00:50:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(inet6_hash);
|