diff mbox series

[net-next,v2,1/3] net: Add a bhash2 table hashed by port + address

Message ID 20220712235310.1935121-2-joannelkoong@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series Add a second bind table hashed by port + address | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 2848 this patch: 2849
netdev/cc_maintainers warning 4 maintainers not CCed: yoshfuji@linux-ipv6.org dccp@vger.kernel.org kuniyu@amazon.co.jp dsahern@kernel.org
netdev/build_clang fail Errors and warnings before: 622 this patch: 624
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 2980 this patch: 2981
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Joanne Koong July 12, 2022, 11:53 p.m. UTC
The current bind hashtable (bhash) is hashed by port only.
In the socket bind path, we have to check for bind conflicts by
traversing the specified port's inet_bind_bucket while holding the
hashbucket's spinlock (see inet_csk_get_port() and
inet_csk_bind_conflict()). In instances where there are tons of
sockets hashed to the same port at different addresses, the bind
conflict check is time-intensive and can cause softirq cpu lockups,
as well as stops new tcp connections since __inet_inherit_port()
also contests for the spinlock.

This patch adds a second bind table, bhash2, that hashes by
port and sk->sk_rcv_saddr (ipv4) and sk->sk_v6_rcv_saddr (ipv6).
Searching the bhash2 table leads to significantly faster conflict
resolution and less time holding the hashbucket spinlock.

Please note a few things:
* There can be the case where the a socket's address changes after it
has been bound. There are two cases where this happens:

  1) The case where there is a bind() call on INADDR_ANY (ipv4) or
  IPV6_ADDR_ANY (ipv6) and then a connect() call. The kernel will
  assign the socket an address when it handles the connect()

  2) In inet_sk_reselect_saddr(), which is called when rebuilding the
  sk header and a few pre-conditions are met (eg rerouting fails).

In these two cases, we need to update the bhash2 table by removing the
entry for the old address, and add a new entry reflecting the updated
address.

* The bhash2 table must have its own lock, even though concurrent
accesses on the same port are protected by the bhash lock. Bhash2 must
have its own lock to protect against cases where sockets on different
ports hash to different bhash hashbuckets but to the same bhash2
hashbucket.

This brings up a few stipulations:
  1) When acquiring both the bhash and the bhash2 lock, the bhash2 lock
  will always be acquired after the bhash lock and released before the
  bhash lock is released.

  2) There are no nested bhash2 hashbucket locks. A bhash2 lock is always
  acquired+released before another bhash2 lock is acquired+released.

* The bhash table cannot be superseded by the bhash2 table because for
bind requests on INADDR_ANY (ipv4) or IPV6_ADDR_ANY (ipv6), every socket
bound to that port must be checked for a potential conflict. The bhash
table is the only source of port->socket associations.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
 include/net/inet_connection_sock.h |   3 +
 include/net/inet_hashtables.h      |  80 ++++++++-
 include/net/sock.h                 |  14 ++
 net/dccp/ipv4.c                    |  24 ++-
 net/dccp/ipv6.c                    |  12 ++
 net/dccp/proto.c                   |  34 +++-
 net/ipv4/af_inet.c                 |  31 +++-
 net/ipv4/inet_connection_sock.c    | 279 ++++++++++++++++++++++-------
 net/ipv4/inet_hashtables.c         | 277 ++++++++++++++++++++++++++--
 net/ipv4/tcp.c                     |  11 +-
 net/ipv4/tcp_ipv4.c                |  21 ++-
 net/ipv6/tcp_ipv6.c                |  12 ++
 12 files changed, 695 insertions(+), 103 deletions(-)

Comments

Paolo Abeni July 14, 2022, 9:08 a.m. UTC | #1
On Tue, 2022-07-12 at 16:53 -0700, Joanne Koong wrote:
> @@ -238,12 +331,23 @@ inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
>  			continue;
>  		head = &hinfo->bhash[inet_bhashfn(net, port,
>  						  hinfo->bhash_size)];
> +		head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
> +
>  		spin_lock_bh(&head->lock);
> +
> +		if (inet_use_bhash2_on_bind(sk)) {
> +			if (inet_bhash2_addr_any_conflict(sk, port, l3mdev, relax, false))
> +				goto next_port;
> +		}
> +
> +		spin_lock(&head2->lock);

Minor nit: it looks like you can compute hash2 but not use it if the
inet_bhash2_addr_any_conflict() call above is unsuccesful. You can move
the inet_bhashfn_portaddr() down. 


[...]

> @@ -675,6 +785,112 @@ void inet_unhash(struct sock *sk)
>  }
>  EXPORT_SYMBOL_GPL(inet_unhash);
>  
> +static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb,
> +				    const struct net *net, unsigned short port,
> +				    int l3mdev, const struct sock *sk)
> +{
> +#if IS_ENABLED(CONFIG_IPV6)
> +	if (sk->sk_family == AF_INET6)
> +		return net_eq(ib2_net(tb), net) && tb->port == port &&
> +			tb->l3mdev == l3mdev &&
> +			ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
> +	else
> +#endif
> +		return net_eq(ib2_net(tb), net) && tb->port == port &&
> +			tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr;
> +}
> +
> +bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net,
> +				      unsigned short port, int l3mdev, const struct sock *sk)
> +{
> +#if IS_ENABLED(CONFIG_IPV6)
> +	struct in6_addr addr_any = {};
> +
> +	if (sk->sk_family == AF_INET6)
> +		return net_eq(ib2_net(tb), net) && tb->port == port &&
> +			tb->l3mdev == l3mdev &&
> +			ipv6_addr_equal(&tb->v6_rcv_saddr, &addr_any);
> +	else
> +#endif
> +		return net_eq(ib2_net(tb), net) && tb->port == port &&
> +			tb->l3mdev == l3mdev && tb->rcv_saddr == 0;
> +}
> +
> +/* The socket's bhash2 hashbucket spinlock must be held when this is called */
> +struct inet_bind2_bucket *
> +inet_bind2_bucket_find(const struct inet_bind_hashbucket *head, const struct net *net,
> +		       unsigned short port, int l3mdev, const struct sock *sk)
> +{
> +	struct inet_bind2_bucket *bhash2 = NULL;
> +
> +	inet_bind_bucket_for_each(bhash2, &head->chain)
> +		if (inet_bind2_bucket_match(bhash2, net, port, l3mdev, sk))
> +			break;
> +
> +	return bhash2;
> +}
> +
> +struct inet_bind_hashbucket *
> +inet_bhash2_addr_any_hashbucket(const struct sock *sk, const struct net *net, int port)
> +{
> +	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
> +	u32 hash;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	struct in6_addr addr_any = {};
> +
> +	if (sk->sk_family == AF_INET6)
> +		hash = ipv6_portaddr_hash(net, &addr_any, port);
> +	else
> +#endif
> +		hash = ipv4_portaddr_hash(net, 0, port);
> +
> +	return &hinfo->bhash2[hash & (hinfo->bhash_size - 1)];
> +}
> +
> +int inet_bhash2_update_saddr(struct inet_bind_hashbucket *prev_saddr, struct sock *sk)
> +{
> +	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
> +	struct inet_bind_hashbucket *head, *head2;
> +	struct inet_bind2_bucket *tb2, *new_tb2;
> +	int l3mdev = inet_sk_bound_l3mdev(sk);
> +	int port = inet_sk(sk)->inet_num;
> +	struct net *net = sock_net(sk);
> +
> +	/* Allocate a bind2 bucket ahead of time to avoid permanently putting
> +	 * the bhash2 table in an inconsistent state if a new tb2 bucket
> +	 * allocation fails.
> +	 */
> +	new_tb2 = kmem_cache_alloc(hinfo->bind2_bucket_cachep, GFP_ATOMIC);
> +	if (!new_tb2)
> +		return -ENOMEM;
> +
> +	head = &hinfo->bhash[inet_bhashfn(net, port,
> +					  hinfo->bhash_size)];

Here 'head' is unused, you can avoid computing the related hash.


Cheers,

Paolo
kernel test robot July 14, 2022, 2:05 p.m. UTC | #2
Hi Joanne,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/Add-a-second-bind-table-hashed-by-port-address/20220713-075808
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 5022e221c98a609e0e5b0a73852c7e3d32f1c545
config: um-i386_defconfig (https://download.01.org/0day-ci/archive/20220714/202207142124.OHu1H8Us-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-3) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/2e20fc25bca52fbc786bbae312df56514c10798d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Joanne-Koong/Add-a-second-bind-table-hashed-by-port-address/20220713-075808
        git checkout 2e20fc25bca52fbc786bbae312df56514c10798d
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=um SUBARCH=i386 SHELL=/bin/bash net/ipv4/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   net/ipv4/inet_hashtables.c: In function 'inet_bhash2_update_saddr':
>> net/ipv4/inet_hashtables.c:853:38: warning: variable 'head' set but not used [-Wunused-but-set-variable]
     853 |         struct inet_bind_hashbucket *head, *head2;
         |                                      ^~~~


vim +/head +853 net/ipv4/inet_hashtables.c

   849	
   850	int inet_bhash2_update_saddr(struct inet_bind_hashbucket *prev_saddr, struct sock *sk)
   851	{
   852		struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
 > 853		struct inet_bind_hashbucket *head, *head2;
   854		struct inet_bind2_bucket *tb2, *new_tb2;
   855		int l3mdev = inet_sk_bound_l3mdev(sk);
   856		int port = inet_sk(sk)->inet_num;
   857		struct net *net = sock_net(sk);
   858	
   859		/* Allocate a bind2 bucket ahead of time to avoid permanently putting
   860		 * the bhash2 table in an inconsistent state if a new tb2 bucket
   861		 * allocation fails.
   862		 */
   863		new_tb2 = kmem_cache_alloc(hinfo->bind2_bucket_cachep, GFP_ATOMIC);
   864		if (!new_tb2)
   865			return -ENOMEM;
   866	
   867		head = &hinfo->bhash[inet_bhashfn(net, port,
   868						  hinfo->bhash_size)];
   869		head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
   870	
   871		spin_lock_bh(&prev_saddr->lock);
   872		__sk_del_bind2_node(sk);
   873		inet_bind2_bucket_destroy(hinfo->bind2_bucket_cachep,
   874					  inet_csk(sk)->icsk_bind2_hash);
   875		spin_unlock_bh(&prev_saddr->lock);
   876	
   877		spin_lock_bh(&head2->lock);
   878		tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, sk);
   879		if (!tb2) {
   880			tb2 = new_tb2;
   881			inet_bind2_bucket_init(tb2, net, head2, port, l3mdev, sk);
   882		}
   883		sk_add_bind2_node(sk, &tb2->owners);
   884		inet_csk(sk)->icsk_bind2_hash = tb2;
   885		spin_unlock_bh(&head2->lock);
   886	
   887		if (tb2 != new_tb2)
   888			kmem_cache_free(hinfo->bind2_bucket_cachep, new_tb2);
   889	
   890		return 0;
   891	}
   892	EXPORT_SYMBOL_GPL(inet_bhash2_update_saddr);
   893
Joanne Koong July 15, 2022, 6:20 p.m. UTC | #3
On Thu, Jul 14, 2022 at 2:08 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On Tue, 2022-07-12 at 16:53 -0700, Joanne Koong wrote:
> > @@ -238,12 +331,23 @@ inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
> >                       continue;
> >               head = &hinfo->bhash[inet_bhashfn(net, port,
> >                                                 hinfo->bhash_size)];
> > +             head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
> > +
> >               spin_lock_bh(&head->lock);
> > +
> > +             if (inet_use_bhash2_on_bind(sk)) {
> > +                     if (inet_bhash2_addr_any_conflict(sk, port, l3mdev, relax, false))
> > +                             goto next_port;
> > +             }
> > +
> > +             spin_lock(&head2->lock);
>
> Minor nit: it looks like you can compute hash2 but not use it if the
> inet_bhash2_addr_any_conflict() call above is unsuccesful. You can move
> the inet_bhashfn_portaddr() down.
I will move this down.
>
>
> [...]
>
> > @@ -675,6 +785,112 @@ void inet_unhash(struct sock *sk)
> >  }
> >  EXPORT_SYMBOL_GPL(inet_unhash);
> >
> > +static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb,
> > +                                 const struct net *net, unsigned short port,
> > +                                 int l3mdev, const struct sock *sk)
> > +{
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +     if (sk->sk_family == AF_INET6)
> > +             return net_eq(ib2_net(tb), net) && tb->port == port &&
> > +                     tb->l3mdev == l3mdev &&
> > +                     ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
> > +     else
> > +#endif
> > +             return net_eq(ib2_net(tb), net) && tb->port == port &&
> > +                     tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr;
> > +}
> > +
> > +bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net,
> > +                                   unsigned short port, int l3mdev, const struct sock *sk)
> > +{
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +     struct in6_addr addr_any = {};
> > +
> > +     if (sk->sk_family == AF_INET6)
> > +             return net_eq(ib2_net(tb), net) && tb->port == port &&
> > +                     tb->l3mdev == l3mdev &&
> > +                     ipv6_addr_equal(&tb->v6_rcv_saddr, &addr_any);
> > +     else
> > +#endif
> > +             return net_eq(ib2_net(tb), net) && tb->port == port &&
> > +                     tb->l3mdev == l3mdev && tb->rcv_saddr == 0;
> > +}
> > +
> > +/* The socket's bhash2 hashbucket spinlock must be held when this is called */
> > +struct inet_bind2_bucket *
> > +inet_bind2_bucket_find(const struct inet_bind_hashbucket *head, const struct net *net,
> > +                    unsigned short port, int l3mdev, const struct sock *sk)
> > +{
> > +     struct inet_bind2_bucket *bhash2 = NULL;
> > +
> > +     inet_bind_bucket_for_each(bhash2, &head->chain)
> > +             if (inet_bind2_bucket_match(bhash2, net, port, l3mdev, sk))
> > +                     break;
> > +
> > +     return bhash2;
> > +}
> > +
> > +struct inet_bind_hashbucket *
> > +inet_bhash2_addr_any_hashbucket(const struct sock *sk, const struct net *net, int port)
> > +{
> > +     struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
> > +     u32 hash;
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +     struct in6_addr addr_any = {};
> > +
> > +     if (sk->sk_family == AF_INET6)
> > +             hash = ipv6_portaddr_hash(net, &addr_any, port);
> > +     else
> > +#endif
> > +             hash = ipv4_portaddr_hash(net, 0, port);
> > +
> > +     return &hinfo->bhash2[hash & (hinfo->bhash_size - 1)];
> > +}
> > +
> > +int inet_bhash2_update_saddr(struct inet_bind_hashbucket *prev_saddr, struct sock *sk)
> > +{
> > +     struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
> > +     struct inet_bind_hashbucket *head, *head2;
> > +     struct inet_bind2_bucket *tb2, *new_tb2;
> > +     int l3mdev = inet_sk_bound_l3mdev(sk);
> > +     int port = inet_sk(sk)->inet_num;
> > +     struct net *net = sock_net(sk);
> > +
> > +     /* Allocate a bind2 bucket ahead of time to avoid permanently putting
> > +      * the bhash2 table in an inconsistent state if a new tb2 bucket
> > +      * allocation fails.
> > +      */
> > +     new_tb2 = kmem_cache_alloc(hinfo->bind2_bucket_cachep, GFP_ATOMIC);
> > +     if (!new_tb2)
> > +             return -ENOMEM;
> > +
> > +     head = &hinfo->bhash[inet_bhashfn(net, port,
> > +                                       hinfo->bhash_size)];
>
> Here 'head' is unused, you can avoid computing the related hash.
>
Ah yes,  you're right. We don't need head here since we already pass
in prev_saddr. Thanks for catching this, I will remove this.
>
> Cheers,
>
> Paolo
Thanks for taking a look, Paolo!
>
kernel test robot July 16, 2022, 2:20 p.m. UTC | #4
Greeting,

FYI, we noticed the following commit (built with gcc-11):

commit: 2e20fc25bca52fbc786bbae312df56514c10798d ("[PATCH net-next v2 1/3] net: Add a bhash2 table hashed by port + address")
url: https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/Add-a-second-bind-table-hashed-by-port-address/20220713-075808
base: https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git 5022e221c98a609e0e5b0a73852c7e3d32f1c545
patch link: https://lore.kernel.org/netdev/20220712235310.1935121-2-joannelkoong@gmail.com

in testcase: boot

on test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):


+-------------------------------------------------------+------------+------------+
|                                                       | 5022e221c9 | 2e20fc25bc |
+-------------------------------------------------------+------------+------------+
| boot_successes                                        | 8          | 0          |
| boot_failures                                         | 0          | 12         |
| BUG:kernel_NULL_pointer_dereference,address           | 0          | 12         |
| Oops:#[##]                                            | 0          | 12         |
| RIP:inet_bhash2_update_saddr                          | 0          | 12         |
| Kernel_panic-not_syncing:Fatal_exception_in_interrupt | 0          | 12         |
+-------------------------------------------------------+------------+------------+


If you fix the issue, kindly add following tag
Reported-by: kernel test robot <oliver.sang@intel.com>


[  247.022450][  T328] BUG: kernel NULL pointer dereference, address: 0000000000000000
[  247.024448][  T328] #PF: supervisor write access in kernel mode
[  247.026159][  T328] #PF: error_code(0x0002) - not-present page
[  247.027743][  T328] PGD 800000014b28a067 P4D 800000014b28a067 PUD 14b289067 PMD 0
[  247.029705][  T328] Oops: 0002 [#1] SMP PTI
[  247.030900][  T328] CPU: 1 PID: 328 Comm: wget Not tainted 5.19.0-rc5-01130-g2e20fc25bca5 #1
[  247.033223][  T328] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-debian-1.16.0-4 04/01/2014
[ 247.035984][ T328] RIP: 0010:inet_bhash2_update_saddr (include/linux/list.h:884 include/net/sock.h:824 net/ipv4/inet_hashtables.c:872) 
[ 247.037623][ T328] Code: 48 8d 83 00 03 00 00 4c 8b a3 f8 02 00 00 48 89 c7 48 89 44 24 28 e8 10 79 01 ff 4c 8b ab 00 03 00 00 4c 89 ef e8 f1 87 01 ff <4d> 89 65 00 4d 85 e4 74 14 e8 93 2b ed fe 49 8d 7c 24 08 e8 d9 87
All code
========
   0:	48 8d 83 00 03 00 00 	lea    0x300(%rbx),%rax
   7:	4c 8b a3 f8 02 00 00 	mov    0x2f8(%rbx),%r12
   e:	48 89 c7             	mov    %rax,%rdi
  11:	48 89 44 24 28       	mov    %rax,0x28(%rsp)
  16:	e8 10 79 01 ff       	callq  0xffffffffff01792b
  1b:	4c 8b ab 00 03 00 00 	mov    0x300(%rbx),%r13
  22:	4c 89 ef             	mov    %r13,%rdi
  25:	e8 f1 87 01 ff       	callq  0xffffffffff01881b
  2a:*	4d 89 65 00          	mov    %r12,0x0(%r13)		<-- trapping instruction
  2e:	4d 85 e4             	test   %r12,%r12
  31:	74 14                	je     0x47
  33:	e8 93 2b ed fe       	callq  0xfffffffffeed2bcb
  38:	49 8d 7c 24 08       	lea    0x8(%r12),%rdi
  3d:	e8                   	.byte 0xe8
  3e:	d9                   	.byte 0xd9
  3f:	87                   	.byte 0x87

Code starting with the faulting instruction
===========================================
   0:	4d 89 65 00          	mov    %r12,0x0(%r13)
   4:	4d 85 e4             	test   %r12,%r12
   7:	74 14                	je     0x1d
   9:	e8 93 2b ed fe       	callq  0xfffffffffeed2ba1
   e:	49 8d 7c 24 08       	lea    0x8(%r12),%rdi
  13:	e8                   	.byte 0xe8
  14:	d9                   	.byte 0xd9
  15:	87                   	.byte 0x87
[  247.062693][  T328] RSP: 0018:ffffc90000ae7bd8 EFLAGS: 00010246
[  247.064435][  T328] RAX: ffff88811673c3e0 RBX: ffff8881168e4600 RCX: ffffffff823fb28f
[  247.066525][  T328] RDX: 0000000000000a28 RSI: 0001ffffffffffff RDI: 0000000000000000
[  247.068479][  T328] RBP: ffffc90000ae7c60 R08: ffffffff8477ff18 R09: 0000000000000000
[  247.070484][  T328] R10: 0000000000000005 R11: 0000000000000000 R12: 0000000000000000
[  247.072457][  T328] R13: 0000000000000000 R14: ffffffff84cefd40 R15: ffffffff84cf29c0
[  247.074463][  T328] FS:  00007f38cc1a6700(0000) GS:ffff88842fd00000(0000) knlGS:0000000000000000
[  247.076798][  T328] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  247.080161][  T328] CR2: 0000000000000000 CR3: 0000000116a32000 CR4: 00000000000006e0
[  247.082224][  T328] Call Trace:
[  247.083152][  T328]  <TASK>
[ 247.083906][ T328] ? write_comp_data (kernel/kcov.c:229) 
[ 247.085183][ T328] tcp_v4_connect (net/ipv4/tcp_ipv4.c:261) 
[ 247.086542][ T328] __inet_stream_connect (net/ipv4/af_inet.c:661) 
[ 247.088103][ T328] ? write_comp_data (kernel/kcov.c:229) 
[ 247.089429][ T328] inet_stream_connect (net/ipv4/af_inet.c:725) 
[ 247.090707][ T328] ? __inet_stream_connect (net/ipv4/af_inet.c:720) 
[ 247.092104][ T328] __sys_connect_file (net/socket.c:1976) 
[ 247.093453][ T328] __sys_connect (net/socket.c:1993) 
[ 247.094902][ T328] ? write_comp_data (kernel/kcov.c:229) 
[ 247.096382][ T328] ? __x64_sys_alarm (kernel/time/itimer.c:306) 
[ 247.097825][ T328] __x64_sys_connect (net/socket.c:2000) 
[ 247.115487][ T328] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) 
[ 247.116792][ T328] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115) 
[  247.118477][  T328] RIP: 0033:0x7f38cb2662e0
[ 247.119521][ T328] Code: 00 31 d2 48 29 c2 64 89 11 48 83 c8 ff eb ea 90 90 90 90 90 90 90 90 90 90 90 83 3d fd 8e 2c 00 00 75 10 b8 2a 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 fe ce 00 00 48 89 04 24
All code
========
   0:	00 31                	add    %dh,(%rcx)
   2:	d2 48 29             	rorb   %cl,0x29(%rax)
   5:	c2 64 89             	retq   $0x8964
   8:	11 48 83             	adc    %ecx,-0x7d(%rax)
   b:	c8 ff eb ea          	enterq $0xebff,$0xea
   f:	90                   	nop
  10:	90                   	nop
  11:	90                   	nop
  12:	90                   	nop
  13:	90                   	nop
  14:	90                   	nop
  15:	90                   	nop
  16:	90                   	nop
  17:	90                   	nop
  18:	90                   	nop
  19:	90                   	nop
  1a:	83 3d fd 8e 2c 00 00 	cmpl   $0x0,0x2c8efd(%rip)        # 0x2c8f1e
  21:	75 10                	jne    0x33
  23:	b8 2a 00 00 00       	mov    $0x2a,%eax
  28:	0f 05                	syscall 
  2a:*	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax		<-- trapping instruction
  30:	73 31                	jae    0x63
  32:	c3                   	retq   
  33:	48 83 ec 08          	sub    $0x8,%rsp
  37:	e8 fe ce 00 00       	callq  0xcf3a
  3c:	48 89 04 24          	mov    %rax,(%rsp)

Code starting with the faulting instruction
===========================================
   0:	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax
   6:	73 31                	jae    0x39
   8:	c3                   	retq   
   9:	48 83 ec 08          	sub    $0x8,%rsp
   d:	e8 fe ce 00 00       	callq  0xcf10
  12:	48 89 04 24          	mov    %rax,(%rsp)
[  247.124379][  T328] RSP: 002b:00007fffffe84038 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
[  247.126935][  T328] RAX: ffffffffffffffda RBX: 00007fffffe840d0 RCX: 00007f38cb2662e0
[  247.128978][  T328] RDX: 0000000000000010 RSI: 00007fffffe840f0 RDI: 0000000000000004
[  247.131142][  T328] RBP: 0000000000000004 R08: 00007fffffe83fa0 R09: 0000000000000001
[  247.133075][  T328] R10: 00007fffffe83dd0 R11: 0000000000000246 R12: 0000000000000050
[  247.135155][  T328] R13: 000000000065ade0 R14: 0000000001549a70 R15: 000000000000002a
[  247.137196][  T328]  </TASK>
[  247.142192][  T328] Modules linked in: bochs drm_vram_helper drm_ttm_helper ttm drm_kms_helper syscopyarea sysfillrect sysimgblt ppdev fb_sys_fops sr_mod drm joydev i2c_piix4 cdrom parport_pc parport
[  247.147469][  T328] CR2: 0000000000000000
[  247.148548][  T328] ---[ end trace 0000000000000000 ]---
[ 247.186378][ T328] RIP: 0010:inet_bhash2_update_saddr (include/linux/list.h:884 include/net/sock.h:824 net/ipv4/inet_hashtables.c:872) 
[ 247.218516][ T328] Code: 48 8d 83 00 03 00 00 4c 8b a3 f8 02 00 00 48 89 c7 48 89 44 24 28 e8 10 79 01 ff 4c 8b ab 00 03 00 00 4c 89 ef e8 f1 87 01 ff <4d> 89 65 00 4d 85 e4 74 14 e8 93 2b ed fe 49 8d 7c 24 08 e8 d9 87
All code
========
   0:	48 8d 83 00 03 00 00 	lea    0x300(%rbx),%rax
   7:	4c 8b a3 f8 02 00 00 	mov    0x2f8(%rbx),%r12
   e:	48 89 c7             	mov    %rax,%rdi
  11:	48 89 44 24 28       	mov    %rax,0x28(%rsp)
  16:	e8 10 79 01 ff       	callq  0xffffffffff01792b
  1b:	4c 8b ab 00 03 00 00 	mov    0x300(%rbx),%r13
  22:	4c 89 ef             	mov    %r13,%rdi
  25:	e8 f1 87 01 ff       	callq  0xffffffffff01881b
  2a:*	4d 89 65 00          	mov    %r12,0x0(%r13)		<-- trapping instruction
  2e:	4d 85 e4             	test   %r12,%r12
  31:	74 14                	je     0x47
  33:	e8 93 2b ed fe       	callq  0xfffffffffeed2bcb
  38:	49 8d 7c 24 08       	lea    0x8(%r12),%rdi
  3d:	e8                   	.byte 0xe8
  3e:	d9                   	.byte 0xd9
  3f:	87                   	.byte 0x87

Code starting with the faulting instruction
===========================================
   0:	4d 89 65 00          	mov    %r12,0x0(%r13)
   4:	4d 85 e4             	test   %r12,%r12
   7:	74 14                	je     0x1d
   9:	e8 93 2b ed fe       	callq  0xfffffffffeed2ba1
   e:	49 8d 7c 24 08       	lea    0x8(%r12),%rdi
  13:	e8                   	.byte 0xe8
  14:	d9                   	.byte 0xd9
  15:	87                   	.byte 0x87


To reproduce:

        # build kernel
	cd linux
	cp config-5.19.0-rc5-01130-g2e20fc25bca5 .config
	make HOSTCC=gcc-11 CC=gcc-11 ARCH=x86_64 olddefconfig prepare modules_prepare bzImage modules
	make HOSTCC=gcc-11 CC=gcc-11 ARCH=x86_64 INSTALL_MOD_PATH=<mod-install-dir> modules_install
	cd <mod-install-dir>
	find lib/ | cpio -o -H newc --quiet | gzip > modules.cgz


        git clone https://github.com/intel/lkp-tests.git
        cd lkp-tests
        bin/lkp qemu -k <bzImage> -m modules.cgz job-script # job-script is attached in this email

        # if come across any failure that blocks the test,
        # please remove ~/.lkp and /lkp dir to run from a clean state.
Joanne Koong July 18, 2022, 9:31 p.m. UTC | #5
On Sat, Jul 16, 2022 at 7:20 AM kernel test robot <oliver.sang@intel.com> wrote:
>
>
>
> Greeting,
>
> FYI, we noticed the following commit (built with gcc-11):
>
> commit: 2e20fc25bca52fbc786bbae312df56514c10798d ("[PATCH net-next v2 1/3] net: Add a bhash2 table hashed by port + address")
> url: https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/Add-a-second-bind-table-hashed-by-port-address/20220713-075808
> base: https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git 5022e221c98a609e0e5b0a73852c7e3d32f1c545
> patch link: https://lore.kernel.org/netdev/20220712235310.1935121-2-joannelkoong@gmail.com
>
> in testcase: boot
>
> on test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G
>
> caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):
>
>
> +-------------------------------------------------------+------------+------------+
> |                                                       | 5022e221c9 | 2e20fc25bc |
> +-------------------------------------------------------+------------+------------+
> | boot_successes                                        | 8          | 0          |
> | boot_failures                                         | 0          | 12         |
> | BUG:kernel_NULL_pointer_dereference,address           | 0          | 12         |
> | Oops:#[##]                                            | 0          | 12         |
> | RIP:inet_bhash2_update_saddr                          | 0          | 12         |
> | Kernel_panic-not_syncing:Fatal_exception_in_interrupt | 0          | 12         |
> +-------------------------------------------------------+------------+------------+
>
>
> If you fix the issue, kindly add following tag
> Reported-by: kernel test robot <oliver.sang@intel.com>
>
I will fix this issue in the next iteration of the patch (if the
previous address was never added to the bhash2 table, then we don't
need to compute the hash for it and remove it from the table). Thanks
for reporting.
>
> [  247.022450][  T328] BUG: kernel NULL pointer dereference, address: 0000000000000000
> [  247.024448][  T328] #PF: supervisor write access in kernel mode
> [  247.026159][  T328] #PF: error_code(0x0002) - not-present page
> [  247.027743][  T328] PGD 800000014b28a067 P4D 800000014b28a067 PUD 14b289067 PMD 0
> [  247.029705][  T328] Oops: 0002 [#1] SMP PTI
> [  247.030900][  T328] CPU: 1 PID: 328 Comm: wget Not tainted 5.19.0-rc5-01130-g2e20fc25bca5 #1
> [  247.033223][  T328] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-debian-1.16.0-4 04/01/2014
> [ 247.035984][ T328] RIP: 0010:inet_bhash2_update_saddr (include/linux/list.h:884 include/net/sock.h:824 net/ipv4/inet_hashtables.c:872)
> [ 247.037623][ T328] Code: 48 8d 83 00 03 00 00 4c 8b a3 f8 02 00 00 48 89 c7 48 89 44 24 28 e8 10 79 01 ff 4c 8b ab 00 03 00 00 4c 89 ef e8 f1 87 01 ff <4d> 89 65 00 4d 85 e4 74 14 e8 93 2b ed fe 49 8d 7c 24 08 e8 d9 87
> All code
> ========
>    0:   48 8d 83 00 03 00 00    lea    0x300(%rbx),%rax
>    7:   4c 8b a3 f8 02 00 00    mov    0x2f8(%rbx),%r12
>    e:   48 89 c7                mov    %rax,%rdi
>   11:   48 89 44 24 28          mov    %rax,0x28(%rsp)
>   16:   e8 10 79 01 ff          callq  0xffffffffff01792b
>   1b:   4c 8b ab 00 03 00 00    mov    0x300(%rbx),%r13
>   22:   4c 89 ef                mov    %r13,%rdi
>   25:   e8 f1 87 01 ff          callq  0xffffffffff01881b
>   2a:*  4d 89 65 00             mov    %r12,0x0(%r13)           <-- trapping instruction
>   2e:   4d 85 e4                test   %r12,%r12
>   31:   74 14                   je     0x47
>   33:   e8 93 2b ed fe          callq  0xfffffffffeed2bcb
>   38:   49 8d 7c 24 08          lea    0x8(%r12),%rdi
>   3d:   e8                      .byte 0xe8
>   3e:   d9                      .byte 0xd9
>   3f:   87                      .byte 0x87
>
> Code starting with the faulting instruction
> ===========================================
>    0:   4d 89 65 00             mov    %r12,0x0(%r13)
>    4:   4d 85 e4                test   %r12,%r12
>    7:   74 14                   je     0x1d
>    9:   e8 93 2b ed fe          callq  0xfffffffffeed2ba1
>    e:   49 8d 7c 24 08          lea    0x8(%r12),%rdi
>   13:   e8                      .byte 0xe8
>   14:   d9                      .byte 0xd9
>   15:   87                      .byte 0x87
> [  247.062693][  T328] RSP: 0018:ffffc90000ae7bd8 EFLAGS: 00010246
> [  247.064435][  T328] RAX: ffff88811673c3e0 RBX: ffff8881168e4600 RCX: ffffffff823fb28f
> [  247.066525][  T328] RDX: 0000000000000a28 RSI: 0001ffffffffffff RDI: 0000000000000000
> [  247.068479][  T328] RBP: ffffc90000ae7c60 R08: ffffffff8477ff18 R09: 0000000000000000
> [  247.070484][  T328] R10: 0000000000000005 R11: 0000000000000000 R12: 0000000000000000
> [  247.072457][  T328] R13: 0000000000000000 R14: ffffffff84cefd40 R15: ffffffff84cf29c0
> [  247.074463][  T328] FS:  00007f38cc1a6700(0000) GS:ffff88842fd00000(0000) knlGS:0000000000000000
> [  247.076798][  T328] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [  247.080161][  T328] CR2: 0000000000000000 CR3: 0000000116a32000 CR4: 00000000000006e0
> [  247.082224][  T328] Call Trace:
> [  247.083152][  T328]  <TASK>
> [ 247.083906][ T328] ? write_comp_data (kernel/kcov.c:229)
> [ 247.085183][ T328] tcp_v4_connect (net/ipv4/tcp_ipv4.c:261)
> [ 247.086542][ T328] __inet_stream_connect (net/ipv4/af_inet.c:661)
> [ 247.088103][ T328] ? write_comp_data (kernel/kcov.c:229)
> [ 247.089429][ T328] inet_stream_connect (net/ipv4/af_inet.c:725)
> [ 247.090707][ T328] ? __inet_stream_connect (net/ipv4/af_inet.c:720)
> [ 247.092104][ T328] __sys_connect_file (net/socket.c:1976)
> [ 247.093453][ T328] __sys_connect (net/socket.c:1993)
> [ 247.094902][ T328] ? write_comp_data (kernel/kcov.c:229)
> [ 247.096382][ T328] ? __x64_sys_alarm (kernel/time/itimer.c:306)
> [ 247.097825][ T328] __x64_sys_connect (net/socket.c:2000)
> [ 247.115487][ T328] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
> [ 247.116792][ T328] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115)
> [  247.118477][  T328] RIP: 0033:0x7f38cb2662e0
> [ 247.119521][ T328] Code: 00 31 d2 48 29 c2 64 89 11 48 83 c8 ff eb ea 90 90 90 90 90 90 90 90 90 90 90 83 3d fd 8e 2c 00 00 75 10 b8 2a 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 fe ce 00 00 48 89 04 24
> All code
> ========
>    0:   00 31                   add    %dh,(%rcx)
>    2:   d2 48 29                rorb   %cl,0x29(%rax)
>    5:   c2 64 89                retq   $0x8964
>    8:   11 48 83                adc    %ecx,-0x7d(%rax)
>    b:   c8 ff eb ea             enterq $0xebff,$0xea
>    f:   90                      nop
>   10:   90                      nop
>   11:   90                      nop
>   12:   90                      nop
>   13:   90                      nop
>   14:   90                      nop
>   15:   90                      nop
>   16:   90                      nop
>   17:   90                      nop
>   18:   90                      nop
>   19:   90                      nop
>   1a:   83 3d fd 8e 2c 00 00    cmpl   $0x0,0x2c8efd(%rip)        # 0x2c8f1e
>   21:   75 10                   jne    0x33
>   23:   b8 2a 00 00 00          mov    $0x2a,%eax
>   28:   0f 05                   syscall
>   2a:*  48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%rax         <-- trapping instruction
>   30:   73 31                   jae    0x63
>   32:   c3                      retq
>   33:   48 83 ec 08             sub    $0x8,%rsp
>   37:   e8 fe ce 00 00          callq  0xcf3a
>   3c:   48 89 04 24             mov    %rax,(%rsp)
>
> Code starting with the faulting instruction
> ===========================================
>    0:   48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%rax
>    6:   73 31                   jae    0x39
>    8:   c3                      retq
>    9:   48 83 ec 08             sub    $0x8,%rsp
>    d:   e8 fe ce 00 00          callq  0xcf10
>   12:   48 89 04 24             mov    %rax,(%rsp)
> [  247.124379][  T328] RSP: 002b:00007fffffe84038 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
> [  247.126935][  T328] RAX: ffffffffffffffda RBX: 00007fffffe840d0 RCX: 00007f38cb2662e0
> [  247.128978][  T328] RDX: 0000000000000010 RSI: 00007fffffe840f0 RDI: 0000000000000004
> [  247.131142][  T328] RBP: 0000000000000004 R08: 00007fffffe83fa0 R09: 0000000000000001
> [  247.133075][  T328] R10: 00007fffffe83dd0 R11: 0000000000000246 R12: 0000000000000050
> [  247.135155][  T328] R13: 000000000065ade0 R14: 0000000001549a70 R15: 000000000000002a
> [  247.137196][  T328]  </TASK>
> [  247.142192][  T328] Modules linked in: bochs drm_vram_helper drm_ttm_helper ttm drm_kms_helper syscopyarea sysfillrect sysimgblt ppdev fb_sys_fops sr_mod drm joydev i2c_piix4 cdrom parport_pc parport
> [  247.147469][  T328] CR2: 0000000000000000
> [  247.148548][  T328] ---[ end trace 0000000000000000 ]---
> [ 247.186378][ T328] RIP: 0010:inet_bhash2_update_saddr (include/linux/list.h:884 include/net/sock.h:824 net/ipv4/inet_hashtables.c:872)
> [ 247.218516][ T328] Code: 48 8d 83 00 03 00 00 4c 8b a3 f8 02 00 00 48 89 c7 48 89 44 24 28 e8 10 79 01 ff 4c 8b ab 00 03 00 00 4c 89 ef e8 f1 87 01 ff <4d> 89 65 00 4d 85 e4 74 14 e8 93 2b ed fe 49 8d 7c 24 08 e8 d9 87
> All code
> ========
>    0:   48 8d 83 00 03 00 00    lea    0x300(%rbx),%rax
>    7:   4c 8b a3 f8 02 00 00    mov    0x2f8(%rbx),%r12
>    e:   48 89 c7                mov    %rax,%rdi
>   11:   48 89 44 24 28          mov    %rax,0x28(%rsp)
>   16:   e8 10 79 01 ff          callq  0xffffffffff01792b
>   1b:   4c 8b ab 00 03 00 00    mov    0x300(%rbx),%r13
>   22:   4c 89 ef                mov    %r13,%rdi
>   25:   e8 f1 87 01 ff          callq  0xffffffffff01881b
>   2a:*  4d 89 65 00             mov    %r12,0x0(%r13)           <-- trapping instruction
>   2e:   4d 85 e4                test   %r12,%r12
>   31:   74 14                   je     0x47
>   33:   e8 93 2b ed fe          callq  0xfffffffffeed2bcb
>   38:   49 8d 7c 24 08          lea    0x8(%r12),%rdi
>   3d:   e8                      .byte 0xe8
>   3e:   d9                      .byte 0xd9
>   3f:   87                      .byte 0x87
>
> Code starting with the faulting instruction
> ===========================================
>    0:   4d 89 65 00             mov    %r12,0x0(%r13)
>    4:   4d 85 e4                test   %r12,%r12
>    7:   74 14                   je     0x1d
>    9:   e8 93 2b ed fe          callq  0xfffffffffeed2ba1
>    e:   49 8d 7c 24 08          lea    0x8(%r12),%rdi
>   13:   e8                      .byte 0xe8
>   14:   d9                      .byte 0xd9
>   15:   87                      .byte 0x87
>
>
> To reproduce:
>
>         # build kernel
>         cd linux
>         cp config-5.19.0-rc5-01130-g2e20fc25bca5 .config
>         make HOSTCC=gcc-11 CC=gcc-11 ARCH=x86_64 olddefconfig prepare modules_prepare bzImage modules
>         make HOSTCC=gcc-11 CC=gcc-11 ARCH=x86_64 INSTALL_MOD_PATH=<mod-install-dir> modules_install
>         cd <mod-install-dir>
>         find lib/ | cpio -o -H newc --quiet | gzip > modules.cgz
>
>
>         git clone https://github.com/intel/lkp-tests.git
>         cd lkp-tests
>         bin/lkp qemu -k <bzImage> -m modules.cgz job-script # job-script is attached in this email
>
>         # if come across any failure that blocks the test,
>         # please remove ~/.lkp and /lkp dir to run from a clean state.
>
>
>
> --
> 0-DAY CI Kernel Test Service
> https://01.org/lkp
>
>
diff mbox series

Patch

diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 85cd695e7fd1..077cd730ce2f 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -25,6 +25,7 @@ 
 #undef INET_CSK_CLEAR_TIMERS
 
 struct inet_bind_bucket;
+struct inet_bind2_bucket;
 struct tcp_congestion_ops;
 
 /*
@@ -57,6 +58,7 @@  struct inet_connection_sock_af_ops {
  *
  * @icsk_accept_queue:	   FIFO of established children
  * @icsk_bind_hash:	   Bind node
+ * @icsk_bind2_hash:	   Bind node in the bhash2 table
  * @icsk_timeout:	   Timeout
  * @icsk_retransmit_timer: Resend (no ack)
  * @icsk_rto:		   Retransmit timeout
@@ -83,6 +85,7 @@  struct inet_connection_sock {
 	struct inet_sock	  icsk_inet;
 	struct request_sock_queue icsk_accept_queue;
 	struct inet_bind_bucket	  *icsk_bind_hash;
+	struct inet_bind2_bucket  *icsk_bind2_hash;
 	unsigned long		  icsk_timeout;
  	struct timer_list	  icsk_retransmit_timer;
  	struct timer_list	  icsk_delack_timer;
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index ebfa3df6f8dc..1e8a6ca5a988 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -23,6 +23,7 @@ 
 
 #include <net/inet_connection_sock.h>
 #include <net/inet_sock.h>
+#include <net/ip.h>
 #include <net/sock.h>
 #include <net/route.h>
 #include <net/tcp_states.h>
@@ -90,7 +91,28 @@  struct inet_bind_bucket {
 	struct hlist_head	owners;
 };
 
-static inline struct net *ib_net(struct inet_bind_bucket *ib)
+struct inet_bind2_bucket {
+	possible_net_t		ib_net;
+	int			l3mdev;
+	unsigned short		port;
+	union {
+#if IS_ENABLED(CONFIG_IPV6)
+		struct in6_addr		v6_rcv_saddr;
+#endif
+		__be32			rcv_saddr;
+	};
+	/* Node in the bhash2 inet_bind_hashbucket chain */
+	struct hlist_node	node;
+	/* List of sockets hashed to this bucket */
+	struct hlist_head	owners;
+};
+
+static inline struct net *ib_net(const struct inet_bind_bucket *ib)
+{
+	return read_pnet(&ib->ib_net);
+}
+
+static inline struct net *ib2_net(const struct inet_bind2_bucket *ib)
 {
 	return read_pnet(&ib->ib_net);
 }
@@ -133,7 +155,14 @@  struct inet_hashinfo {
 	 * TCP hash as well as the others for fast bind/connect.
 	 */
 	struct kmem_cache		*bind_bucket_cachep;
+	/* This bind table is hashed by local port */
 	struct inet_bind_hashbucket	*bhash;
+	struct kmem_cache		*bind2_bucket_cachep;
+	/* This bind table is hashed by local port and sk->sk_rcv_saddr (ipv4)
+	 * or sk->sk_v6_rcv_saddr (ipv6). This 2nd bind table is used
+	 * primarily for expediting bind conflict resolution.
+	 */
+	struct inet_bind_hashbucket	*bhash2;
 	unsigned int			bhash_size;
 
 	/* The 2nd listener table hashed by local port and address */
@@ -193,14 +222,61 @@  inet_bind_bucket_create(struct kmem_cache *cachep, struct net *net,
 void inet_bind_bucket_destroy(struct kmem_cache *cachep,
 			      struct inet_bind_bucket *tb);
 
+bool inet_bind_bucket_match(const struct inet_bind_bucket *tb,
+			    const struct net *net, unsigned short port,
+			    int l3mdev);
+
+struct inet_bind2_bucket *
+inet_bind2_bucket_create(struct kmem_cache *cachep, struct net *net,
+			 struct inet_bind_hashbucket *head,
+			 unsigned short port, int l3mdev,
+			 const struct sock *sk);
+
+void inet_bind2_bucket_destroy(struct kmem_cache *cachep,
+			       struct inet_bind2_bucket *tb);
+
+struct inet_bind2_bucket *
+inet_bind2_bucket_find(const struct inet_bind_hashbucket *head,
+		       const struct net *net,
+		       unsigned short port, int l3mdev,
+		       const struct sock *sk);
+
+bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb,
+				      const struct net *net, unsigned short port,
+				      int l3mdev, const struct sock *sk);
+
 static inline u32 inet_bhashfn(const struct net *net, const __u16 lport,
 			       const u32 bhash_size)
 {
 	return (lport + net_hash_mix(net)) & (bhash_size - 1);
 }
 
+static inline struct inet_bind_hashbucket *
+inet_bhashfn_portaddr(const struct inet_hashinfo *hinfo, const struct sock *sk,
+		      const struct net *net, unsigned short port)
+{
+	u32 hash;
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6)
+		hash = ipv6_portaddr_hash(net, &sk->sk_v6_rcv_saddr, port);
+	else
+#endif
+		hash = ipv4_portaddr_hash(net, sk->sk_rcv_saddr, port);
+	return &hinfo->bhash2[hash & (hinfo->bhash_size - 1)];
+}
+
+struct inet_bind_hashbucket *
+inet_bhash2_addr_any_hashbucket(const struct sock *sk, const struct net *net, int port);
+
+/* This should be called whenever a socket's sk_rcv_saddr (ipv4) or
+ * sk_v6_rcv_saddr (ipv6) changes after it has been binded. The socket's
+ * rcv_saddr field should already have been updated when this is called.
+ */
+int inet_bhash2_update_saddr(struct inet_bind_hashbucket *prev_saddr, struct sock *sk);
+
 void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
-		    const unsigned short snum);
+		    struct inet_bind2_bucket *tb2, unsigned short port);
 
 /* Caller must disable local BH processing. */
 int __inet_inherit_port(const struct sock *sk, struct sock *child);
diff --git a/include/net/sock.h b/include/net/sock.h
index 0dd43c3df49b..09ed6515bb95 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -348,6 +348,7 @@  struct sk_filter;
   *	@sk_txtime_report_errors: set report errors mode for SO_TXTIME
   *	@sk_txtime_unused: unused txtime flags
   *	@ns_tracker: tracker for netns reference
+  *	@sk_bind2_node: bind node in the bhash2 table
   */
 struct sock {
 	/*
@@ -537,6 +538,7 @@  struct sock {
 #endif
 	struct rcu_head		sk_rcu;
 	netns_tracker		ns_tracker;
+	struct hlist_node	sk_bind2_node;
 };
 
 enum sk_pacing {
@@ -817,6 +819,16 @@  static inline void sk_add_bind_node(struct sock *sk,
 	hlist_add_head(&sk->sk_bind_node, list);
 }
 
+static inline void __sk_del_bind2_node(struct sock *sk)
+{
+	__hlist_del(&sk->sk_bind2_node);
+}
+
+static inline void sk_add_bind2_node(struct sock *sk, struct hlist_head *list)
+{
+	hlist_add_head(&sk->sk_bind2_node, list);
+}
+
 #define sk_for_each(__sk, list) \
 	hlist_for_each_entry(__sk, list, sk_node)
 #define sk_for_each_rcu(__sk, list) \
@@ -834,6 +846,8 @@  static inline void sk_add_bind_node(struct sock *sk,
 	hlist_for_each_entry_safe(__sk, tmp, list, sk_node)
 #define sk_for_each_bound(__sk, list) \
 	hlist_for_each_entry(__sk, list, sk_bind_node)
+#define sk_for_each_bound_bhash2(__sk, list) \
+	hlist_for_each_entry(__sk, list, sk_bind2_node)
 
 /**
  * sk_for_each_entry_offset_rcu - iterate over a list at a given struct offset
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index da6e3b20cd75..7958f5d355f3 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -45,14 +45,15 @@  static unsigned int dccp_v4_pernet_id __read_mostly;
 int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
 	const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
+	struct inet_bind_hashbucket *prev_addr_hashbucket = NULL;
+	__be32 daddr, nexthop, prev_sk_rcv_saddr;
 	struct inet_sock *inet = inet_sk(sk);
 	struct dccp_sock *dp = dccp_sk(sk);
+	struct ip_options_rcu *inet_opt;
 	__be16 orig_sport, orig_dport;
-	__be32 daddr, nexthop;
 	struct flowi4 *fl4;
 	struct rtable *rt;
 	int err;
-	struct ip_options_rcu *inet_opt;
 
 	dp->dccps_role = DCCP_ROLE_CLIENT;
 
@@ -89,9 +90,26 @@  int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 	if (inet_opt == NULL || !inet_opt->opt.srr)
 		daddr = fl4->daddr;
 
-	if (inet->inet_saddr == 0)
+	if (inet->inet_saddr == 0) {
+		prev_addr_hashbucket = inet_bhashfn_portaddr(&dccp_hashinfo,
+							     sk, sock_net(sk),
+							     inet->inet_num);
+		prev_sk_rcv_saddr = sk->sk_rcv_saddr;
 		inet->inet_saddr = fl4->saddr;
+	}
+
 	sk_rcv_saddr_set(sk, inet->inet_saddr);
+
+	if (prev_addr_hashbucket) {
+		err = inet_bhash2_update_saddr(prev_addr_hashbucket, sk);
+		if (err) {
+			inet->inet_saddr = 0;
+			sk_rcv_saddr_set(sk, prev_sk_rcv_saddr);
+			ip_rt_put(rt);
+			return err;
+		}
+	}
+
 	inet->inet_dport = usin->sin_port;
 	sk_daddr_set(sk, daddr);
 
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index fd44638ec16b..83843aea173c 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -934,8 +934,20 @@  static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 	}
 
 	if (saddr == NULL) {
+		struct in6_addr prev_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
+		struct inet_bind_hashbucket *prev_addr_hashbucket;
+
+		prev_addr_hashbucket = inet_bhashfn_portaddr(&dccp_hashinfo,
+							     sk, sock_net(sk),
+							     inet->inet_num);
 		saddr = &fl6.saddr;
 		sk->sk_v6_rcv_saddr = *saddr;
+
+		err = inet_bhash2_update_saddr(prev_addr_hashbucket, sk);
+		if (err) {
+			sk->sk_v6_rcv_saddr = prev_v6_rcv_saddr;
+			goto failure;
+		}
 	}
 
 	/* set the source address */
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index eb8e128e43e8..f4f2ad5f9c08 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -1120,6 +1120,12 @@  static int __init dccp_init(void)
 				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
 	if (!dccp_hashinfo.bind_bucket_cachep)
 		goto out_free_hashinfo2;
+	dccp_hashinfo.bind2_bucket_cachep =
+		kmem_cache_create("dccp_bind2_bucket",
+				  sizeof(struct inet_bind2_bucket), 0,
+				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
+	if (!dccp_hashinfo.bind2_bucket_cachep)
+		goto out_free_bind_bucket_cachep;
 
 	/*
 	 * Size and allocate the main established and bind bucket
@@ -1150,7 +1156,7 @@  static int __init dccp_init(void)
 
 	if (!dccp_hashinfo.ehash) {
 		DCCP_CRIT("Failed to allocate DCCP established hash table");
-		goto out_free_bind_bucket_cachep;
+		goto out_free_bind2_bucket_cachep;
 	}
 
 	for (i = 0; i <= dccp_hashinfo.ehash_mask; i++)
@@ -1176,14 +1182,24 @@  static int __init dccp_init(void)
 		goto out_free_dccp_locks;
 	}
 
+	dccp_hashinfo.bhash2 = (struct inet_bind_hashbucket *)
+		__get_free_pages(GFP_ATOMIC | __GFP_NOWARN, bhash_order);
+
+	if (!dccp_hashinfo.bhash2) {
+		DCCP_CRIT("Failed to allocate DCCP bind2 hash table");
+		goto out_free_dccp_bhash;
+	}
+
 	for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
 		spin_lock_init(&dccp_hashinfo.bhash[i].lock);
 		INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
+		spin_lock_init(&dccp_hashinfo.bhash2[i].lock);
+		INIT_HLIST_HEAD(&dccp_hashinfo.bhash2[i].chain);
 	}
 
 	rc = dccp_mib_init();
 	if (rc)
-		goto out_free_dccp_bhash;
+		goto out_free_dccp_bhash2;
 
 	rc = dccp_ackvec_init();
 	if (rc)
@@ -1207,30 +1223,38 @@  static int __init dccp_init(void)
 	dccp_ackvec_exit();
 out_free_dccp_mib:
 	dccp_mib_exit();
+out_free_dccp_bhash2:
+	free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order);
 out_free_dccp_bhash:
 	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
 out_free_dccp_locks:
 	inet_ehash_locks_free(&dccp_hashinfo);
 out_free_dccp_ehash:
 	free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
+out_free_bind2_bucket_cachep:
+	kmem_cache_destroy(dccp_hashinfo.bind2_bucket_cachep);
 out_free_bind_bucket_cachep:
 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
 out_free_hashinfo2:
 	inet_hashinfo2_free_mod(&dccp_hashinfo);
 out_fail:
 	dccp_hashinfo.bhash = NULL;
+	dccp_hashinfo.bhash2 = NULL;
 	dccp_hashinfo.ehash = NULL;
 	dccp_hashinfo.bind_bucket_cachep = NULL;
+	dccp_hashinfo.bind2_bucket_cachep = NULL;
 	return rc;
 }
 
 static void __exit dccp_fini(void)
 {
+	int bhash_order = get_order(dccp_hashinfo.bhash_size *
+				    sizeof(struct inet_bind_hashbucket));
+
 	ccid_cleanup_builtins();
 	dccp_mib_exit();
-	free_pages((unsigned long)dccp_hashinfo.bhash,
-		   get_order(dccp_hashinfo.bhash_size *
-			     sizeof(struct inet_bind_hashbucket)));
+	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
+	free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order);
 	free_pages((unsigned long)dccp_hashinfo.ehash,
 		   get_order((dccp_hashinfo.ehash_mask + 1) *
 			     sizeof(struct inet_ehash_bucket)));
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 7abd652a558f..ab8a5c3dc507 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1219,13 +1219,15 @@  EXPORT_SYMBOL(inet_unregister_protosw);
 
 static int inet_sk_reselect_saddr(struct sock *sk)
 {
+	struct inet_bind_hashbucket *prev_addr_hashbucket;
 	struct inet_sock *inet = inet_sk(sk);
 	__be32 old_saddr = inet->inet_saddr;
 	__be32 daddr = inet->inet_daddr;
+	struct ip_options_rcu *inet_opt;
 	struct flowi4 *fl4;
 	struct rtable *rt;
 	__be32 new_saddr;
-	struct ip_options_rcu *inet_opt;
+	int err;
 
 	inet_opt = rcu_dereference_protected(inet->inet_opt,
 					     lockdep_sock_is_held(sk));
@@ -1240,20 +1242,33 @@  static int inet_sk_reselect_saddr(struct sock *sk)
 	if (IS_ERR(rt))
 		return PTR_ERR(rt);
 
-	sk_setup_caps(sk, &rt->dst);
-
 	new_saddr = fl4->saddr;
 
-	if (new_saddr == old_saddr)
+	if (new_saddr == old_saddr) {
+		sk_setup_caps(sk, &rt->dst);
 		return 0;
-
-	if (sock_net(sk)->ipv4.sysctl_ip_dynaddr > 1) {
-		pr_info("%s(): shifting inet->saddr from %pI4 to %pI4\n",
-			__func__, &old_saddr, &new_saddr);
 	}
 
+	prev_addr_hashbucket = inet_bhashfn_portaddr(sk->sk_prot->h.hashinfo,
+						     sk, sock_net(sk),
+						     inet->inet_num);
+
 	inet->inet_saddr = inet->inet_rcv_saddr = new_saddr;
 
+	err = inet_bhash2_update_saddr(prev_addr_hashbucket, sk);
+	if (err) {
+		inet->inet_saddr = old_saddr;
+		inet->inet_rcv_saddr = old_saddr;
+		ip_rt_put(rt);
+		return err;
+	}
+
+	sk_setup_caps(sk, &rt->dst);
+
+	if (sock_net(sk)->ipv4.sysctl_ip_dynaddr > 1)
+		pr_info("%s(): shifting inet->saddr from %pI4 to %pI4\n",
+			__func__, &old_saddr, &new_saddr);
+
 	/*
 	 * XXX The only one ugly spot where we need to
 	 * XXX really change the sockets identity after
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 53f5f956d948..182b4059d0b6 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -130,14 +130,75 @@  void inet_get_local_port_range(struct net *net, int *low, int *high)
 }
 EXPORT_SYMBOL(inet_get_local_port_range);
 
+static bool inet_use_bhash2_on_bind(const struct sock *sk)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6) {
+		int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr);
+
+		return addr_type != IPV6_ADDR_ANY &&
+			addr_type != IPV6_ADDR_MAPPED;
+	}
+#endif
+	return sk->sk_rcv_saddr != htonl(INADDR_ANY);
+}
+
+static bool inet_bind_conflict(const struct sock *sk, struct sock *sk2,
+			       kuid_t sk_uid, bool relax,
+			       bool reuseport_cb_ok, bool reuseport_ok)
+{
+	int bound_dev_if2;
+
+	if (sk == sk2)
+		return false;
+
+	bound_dev_if2 = READ_ONCE(sk2->sk_bound_dev_if);
+
+	if (!sk->sk_bound_dev_if || !bound_dev_if2 ||
+	    sk->sk_bound_dev_if == bound_dev_if2) {
+		if (sk->sk_reuse && sk2->sk_reuse &&
+		    sk2->sk_state != TCP_LISTEN) {
+			if (!relax || (!reuseport_ok && sk->sk_reuseport &&
+				       sk2->sk_reuseport && reuseport_cb_ok &&
+				       (sk2->sk_state == TCP_TIME_WAIT ||
+					uid_eq(sk_uid, sock_i_uid(sk2)))))
+				return true;
+		} else if (!reuseport_ok || !sk->sk_reuseport ||
+			   !sk2->sk_reuseport || !reuseport_cb_ok ||
+			   (sk2->sk_state != TCP_TIME_WAIT &&
+			    !uid_eq(sk_uid, sock_i_uid(sk2)))) {
+			return true;
+		}
+	}
+	return false;
+}
+
+static bool inet_bhash2_conflict(const struct sock *sk,
+				 const struct inet_bind2_bucket *tb2,
+				 kuid_t sk_uid,
+				 bool relax, bool reuseport_cb_ok,
+				 bool reuseport_ok)
+{
+	struct sock *sk2;
+
+	sk_for_each_bound_bhash2(sk2, &tb2->owners) {
+		if (sk->sk_family == AF_INET && ipv6_only_sock(sk2))
+			continue;
+
+		if (inet_bind_conflict(sk, sk2, sk_uid, relax,
+				       reuseport_cb_ok, reuseport_ok))
+			return true;
+	}
+	return false;
+}
+
+/* This should be called only when the tb and tb2 hashbuckets' locks are held */
 static int inet_csk_bind_conflict(const struct sock *sk,
 				  const struct inet_bind_bucket *tb,
+				  const struct inet_bind2_bucket *tb2, /* may be null */
 				  bool relax, bool reuseport_ok)
 {
-	struct sock *sk2;
 	bool reuseport_cb_ok;
-	bool reuse = sk->sk_reuse;
-	bool reuseport = !!sk->sk_reuseport;
 	struct sock_reuseport *reuseport_cb;
 	kuid_t uid = sock_i_uid((struct sock *)sk);
 
@@ -150,55 +211,87 @@  static int inet_csk_bind_conflict(const struct sock *sk,
 	/*
 	 * Unlike other sk lookup places we do not check
 	 * for sk_net here, since _all_ the socks listed
-	 * in tb->owners list belong to the same net - the
-	 * one this bucket belongs to.
+	 * in tb->owners and tb2->owners list belong
+	 * to the same net - the one this bucket belongs to.
 	 */
 
-	sk_for_each_bound(sk2, &tb->owners) {
-		int bound_dev_if2;
+	if (!inet_use_bhash2_on_bind(sk)) {
+		struct sock *sk2;
 
-		if (sk == sk2)
-			continue;
-		bound_dev_if2 = READ_ONCE(sk2->sk_bound_dev_if);
-		if ((!sk->sk_bound_dev_if ||
-		     !bound_dev_if2 ||
-		     sk->sk_bound_dev_if == bound_dev_if2)) {
-			if (reuse && sk2->sk_reuse &&
-			    sk2->sk_state != TCP_LISTEN) {
-				if ((!relax ||
-				     (!reuseport_ok &&
-				      reuseport && sk2->sk_reuseport &&
-				      reuseport_cb_ok &&
-				      (sk2->sk_state == TCP_TIME_WAIT ||
-				       uid_eq(uid, sock_i_uid(sk2))))) &&
-				    inet_rcv_saddr_equal(sk, sk2, true))
-					break;
-			} else if (!reuseport_ok ||
-				   !reuseport || !sk2->sk_reuseport ||
-				   !reuseport_cb_ok ||
-				   (sk2->sk_state != TCP_TIME_WAIT &&
-				    !uid_eq(uid, sock_i_uid(sk2)))) {
-				if (inet_rcv_saddr_equal(sk, sk2, true))
-					break;
-			}
-		}
+		sk_for_each_bound(sk2, &tb->owners)
+			if (inet_bind_conflict(sk, sk2, uid, relax,
+					       reuseport_cb_ok, reuseport_ok) &&
+			    inet_rcv_saddr_equal(sk, sk2, true))
+				return true;
+
+		return false;
+	}
+
+	/* Conflicts with an existing IPV6_ADDR_ANY (if ipv6) or INADDR_ANY (if
+	 * ipv4) should have been checked already. We need to do these two
+	 * checks separately because their spinlocks have to be acquired/released
+	 * independently of each other, to prevent possible deadlocks
+	 */
+	return tb2 && inet_bhash2_conflict(sk, tb2, uid, relax, reuseport_cb_ok,
+					   reuseport_ok);
+}
+
+/* Determine if there is a bind conflict with an existing IPV6_ADDR_ANY (if ipv6) or
+ * INADDR_ANY (if ipv4) socket.
+ *
+ * Caller must hold bhash hashbucket lock with local bh disabled, to protect
+ * against concurrent binds on the port for addr any
+ */
+static bool inet_bhash2_addr_any_conflict(const struct sock *sk, int port, int l3mdev,
+					  bool relax, bool reuseport_ok)
+{
+	kuid_t uid = sock_i_uid((struct sock *)sk);
+	const struct net *net = sock_net(sk);
+	struct sock_reuseport *reuseport_cb;
+	struct inet_bind_hashbucket *head2;
+	struct inet_bind2_bucket *tb2;
+	bool reuseport_cb_ok;
+
+	rcu_read_lock();
+	reuseport_cb = rcu_dereference(sk->sk_reuseport_cb);
+	/* paired with WRITE_ONCE() in __reuseport_(add|detach)_closed_sock */
+	reuseport_cb_ok = !reuseport_cb || READ_ONCE(reuseport_cb->num_closed_socks);
+	rcu_read_unlock();
+
+	head2 = inet_bhash2_addr_any_hashbucket(sk, net, port);
+
+	spin_lock(&head2->lock);
+
+	inet_bind_bucket_for_each(tb2, &head2->chain)
+		if (inet_bind2_bucket_match_addr_any(tb2, net, port, l3mdev, sk))
+			break;
+
+	if (tb2 && inet_bhash2_conflict(sk, tb2, uid, relax, reuseport_cb_ok,
+					reuseport_ok)) {
+		spin_unlock(&head2->lock);
+		return true;
 	}
-	return sk2 != NULL;
+
+	spin_unlock(&head2->lock);
+	return false;
 }
 
 /*
- * Find an open port number for the socket.  Returns with the
- * inet_bind_hashbucket lock held.
+ * Find an open port number for the socket. Returns with the
+ * inet_bind_hashbucket locks held if successful.
  */
 static struct inet_bind_hashbucket *
-inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *port_ret)
+inet_csk_find_open_port(const struct sock *sk, struct inet_bind_bucket **tb_ret,
+			struct inet_bind2_bucket **tb2_ret,
+			struct inet_bind_hashbucket **head2_ret, int *port_ret)
 {
 	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
 	int port = 0;
-	struct inet_bind_hashbucket *head;
+	struct inet_bind_hashbucket *head, *head2;
 	struct net *net = sock_net(sk);
 	bool relax = false;
 	int i, low, high, attempt_half;
+	struct inet_bind2_bucket *tb2;
 	struct inet_bind_bucket *tb;
 	u32 remaining, offset;
 	int l3mdev;
@@ -238,12 +331,23 @@  inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
 			continue;
 		head = &hinfo->bhash[inet_bhashfn(net, port,
 						  hinfo->bhash_size)];
+		head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
+
 		spin_lock_bh(&head->lock);
+
+		if (inet_use_bhash2_on_bind(sk)) {
+			if (inet_bhash2_addr_any_conflict(sk, port, l3mdev, relax, false))
+				goto next_port;
+		}
+
+		spin_lock(&head2->lock);
+		tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, sk);
 		inet_bind_bucket_for_each(tb, &head->chain)
-			if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
-			    tb->port == port) {
-				if (!inet_csk_bind_conflict(sk, tb, relax, false))
+			if (inet_bind_bucket_match(tb, net, port, l3mdev)) {
+				if (!inet_csk_bind_conflict(sk, tb, tb2,
+							    relax, false))
 					goto success;
+				spin_unlock(&head2->lock);
 				goto next_port;
 			}
 		tb = NULL;
@@ -272,6 +376,8 @@  inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
 success:
 	*port_ret = port;
 	*tb_ret = tb;
+	*tb2_ret = tb2;
+	*head2_ret = head2;
 	return head;
 }
 
@@ -368,53 +474,95 @@  int inet_csk_get_port(struct sock *sk, unsigned short snum)
 	bool reuse = sk->sk_reuse && sk->sk_state != TCP_LISTEN;
 	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
 	int ret = 1, port = snum;
-	struct inet_bind_hashbucket *head;
 	struct net *net = sock_net(sk);
+	bool found_port = false, check_bind_conflict = true;
+	bool bhash_created = false, bhash2_created = false;
+	struct inet_bind_hashbucket *head, *head2;
+	struct inet_bind2_bucket *tb2 = NULL;
 	struct inet_bind_bucket *tb = NULL;
+	bool head2_lock_acquired = false;
 	int l3mdev;
 
 	l3mdev = inet_sk_bound_l3mdev(sk);
 
 	if (!port) {
-		head = inet_csk_find_open_port(sk, &tb, &port);
+		head = inet_csk_find_open_port(sk, &tb, &tb2, &head2, &port);
 		if (!head)
 			return ret;
+
+		head2_lock_acquired = true;
+
+		if (tb && tb2)
+			goto success;
+		found_port = true;
+	} else {
+		head = &hinfo->bhash[inet_bhashfn(net, port,
+						  hinfo->bhash_size)];
+		spin_lock_bh(&head->lock);
+		inet_bind_bucket_for_each(tb, &head->chain)
+			if (inet_bind_bucket_match(tb, net, port, l3mdev))
+				break;
+	}
+
+	if (!tb) {
+		tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, net,
+					     head, port, l3mdev);
 		if (!tb)
-			goto tb_not_found;
-		goto success;
+			goto fail_unlock;
+		bhash_created = true;
 	}
-	head = &hinfo->bhash[inet_bhashfn(net, port,
-					  hinfo->bhash_size)];
-	spin_lock_bh(&head->lock);
-	inet_bind_bucket_for_each(tb, &head->chain)
-		if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
-		    tb->port == port)
-			goto tb_found;
-tb_not_found:
-	tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
-				     net, head, port, l3mdev);
-	if (!tb)
-		goto fail_unlock;
-tb_found:
-	if (!hlist_empty(&tb->owners)) {
-		if (sk->sk_reuse == SK_FORCE_REUSE)
-			goto success;
 
-		if ((tb->fastreuse > 0 && reuse) ||
-		    sk_reuseport_match(tb, sk))
-			goto success;
-		if (inet_csk_bind_conflict(sk, tb, true, true))
+	if (!found_port) {
+		if (!hlist_empty(&tb->owners)) {
+			if (sk->sk_reuse == SK_FORCE_REUSE ||
+			    (tb->fastreuse > 0 && reuse) ||
+			    sk_reuseport_match(tb, sk))
+				check_bind_conflict = false;
+		}
+
+		if (check_bind_conflict && inet_use_bhash2_on_bind(sk)) {
+			if (inet_bhash2_addr_any_conflict(sk, port, l3mdev, true, true))
+				goto fail_unlock;
+		}
+
+		head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
+		spin_lock(&head2->lock);
+		head2_lock_acquired = true;
+		tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, sk);
+	}
+
+	if (!tb2) {
+		tb2 = inet_bind2_bucket_create(hinfo->bind2_bucket_cachep,
+					       net, head2, port, l3mdev, sk);
+		if (!tb2)
 			goto fail_unlock;
+		bhash2_created = true;
 	}
+
+	if (!found_port && check_bind_conflict) {
+		if (inet_csk_bind_conflict(sk, tb, tb2, true, true))
+			goto fail_unlock;
+	}
+
 success:
 	inet_csk_update_fastreuse(tb, sk);
 
 	if (!inet_csk(sk)->icsk_bind_hash)
-		inet_bind_hash(sk, tb, port);
+		inet_bind_hash(sk, tb, tb2, port);
 	WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);
+	WARN_ON(inet_csk(sk)->icsk_bind2_hash != tb2);
 	ret = 0;
 
 fail_unlock:
+	if (ret) {
+		if (bhash_created)
+			inet_bind_bucket_destroy(hinfo->bind_bucket_cachep, tb);
+		if (bhash2_created)
+			inet_bind2_bucket_destroy(hinfo->bind2_bucket_cachep,
+						  tb2);
+	}
+	if (head2_lock_acquired)
+		spin_unlock(&head2->lock);
 	spin_unlock_bh(&head->lock);
 	return ret;
 }
@@ -961,6 +1109,7 @@  struct sock *inet_csk_clone_lock(const struct sock *sk,
 
 		inet_sk_set_state(newsk, TCP_SYN_RECV);
 		newicsk->icsk_bind_hash = NULL;
+		newicsk->icsk_bind2_hash = NULL;
 
 		inet_sk(newsk)->inet_dport = inet_rsk(req)->ir_rmt_port;
 		inet_sk(newsk)->inet_num = inet_rsk(req)->ir_num;
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index b9d995b5ce24..d9780f4f3ca2 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -92,12 +92,75 @@  void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket
 	}
 }
 
+bool inet_bind_bucket_match(const struct inet_bind_bucket *tb, const struct net *net,
+			    unsigned short port, int l3mdev)
+{
+	return net_eq(ib_net(tb), net) && tb->port == port &&
+		tb->l3mdev == l3mdev;
+}
+
+static void inet_bind2_bucket_init(struct inet_bind2_bucket *tb,
+				   struct net *net,
+				   struct inet_bind_hashbucket *head,
+				   unsigned short port, int l3mdev,
+				   const struct sock *sk)
+{
+	write_pnet(&tb->ib_net, net);
+	tb->l3mdev    = l3mdev;
+	tb->port      = port;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6)
+		tb->v6_rcv_saddr = sk->sk_v6_rcv_saddr;
+	else
+#endif
+		tb->rcv_saddr = sk->sk_rcv_saddr;
+	INIT_HLIST_HEAD(&tb->owners);
+	hlist_add_head(&tb->node, &head->chain);
+}
+
+struct inet_bind2_bucket *inet_bind2_bucket_create(struct kmem_cache *cachep,
+						   struct net *net,
+						   struct inet_bind_hashbucket *head,
+						   unsigned short port,
+						   int l3mdev,
+						   const struct sock *sk)
+{
+	struct inet_bind2_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
+
+	if (tb)
+		inet_bind2_bucket_init(tb, net, head, port, l3mdev, sk);
+
+	return tb;
+}
+
+/* Caller must hold hashbucket lock for this tb with local BH disabled */
+void inet_bind2_bucket_destroy(struct kmem_cache *cachep, struct inet_bind2_bucket *tb)
+{
+	if (hlist_empty(&tb->owners)) {
+		__hlist_del(&tb->node);
+		kmem_cache_free(cachep, tb);
+	}
+}
+
+static bool inet_bind2_bucket_addr_match(const struct inet_bind2_bucket *tb2,
+					 const struct sock *sk)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6)
+		return ipv6_addr_equal(&tb2->v6_rcv_saddr,
+				       &sk->sk_v6_rcv_saddr);
+#endif
+	return tb2->rcv_saddr == sk->sk_rcv_saddr;
+}
+
 void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb,
-		    const unsigned short snum)
+		    struct inet_bind2_bucket *tb2, unsigned short port)
 {
-	inet_sk(sk)->inet_num = snum;
+	inet_sk(sk)->inet_num = port;
 	sk_add_bind_node(sk, &tb->owners);
 	inet_csk(sk)->icsk_bind_hash = tb;
+	sk_add_bind2_node(sk, &tb2->owners);
+	inet_csk(sk)->icsk_bind2_hash = tb2;
 }
 
 /*
@@ -109,6 +172,10 @@  static void __inet_put_port(struct sock *sk)
 	const int bhash = inet_bhashfn(sock_net(sk), inet_sk(sk)->inet_num,
 			hashinfo->bhash_size);
 	struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash];
+	struct inet_bind_hashbucket *head2 =
+		inet_bhashfn_portaddr(hashinfo, sk, sock_net(sk),
+				      inet_sk(sk)->inet_num);
+	struct inet_bind2_bucket *tb2;
 	struct inet_bind_bucket *tb;
 
 	spin_lock(&head->lock);
@@ -117,6 +184,16 @@  static void __inet_put_port(struct sock *sk)
 	inet_csk(sk)->icsk_bind_hash = NULL;
 	inet_sk(sk)->inet_num = 0;
 	inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb);
+
+	spin_lock(&head2->lock);
+	if (inet_csk(sk)->icsk_bind2_hash) {
+		tb2 = inet_csk(sk)->icsk_bind2_hash;
+		__sk_del_bind2_node(sk);
+		inet_csk(sk)->icsk_bind2_hash = NULL;
+		inet_bind2_bucket_destroy(hashinfo->bind2_bucket_cachep, tb2);
+	}
+	spin_unlock(&head2->lock);
+
 	spin_unlock(&head->lock);
 }
 
@@ -133,14 +210,23 @@  int __inet_inherit_port(const struct sock *sk, struct sock *child)
 	struct inet_hashinfo *table = sk->sk_prot->h.hashinfo;
 	unsigned short port = inet_sk(child)->inet_num;
 	const int bhash = inet_bhashfn(sock_net(sk), port,
-			table->bhash_size);
+				       table->bhash_size);
 	struct inet_bind_hashbucket *head = &table->bhash[bhash];
+	struct inet_bind_hashbucket *head2 =
+		inet_bhashfn_portaddr(table, child, sock_net(sk), port);
+	bool created_inet_bind_bucket = false;
+	bool update_fastreuse = false;
+	struct net *net = sock_net(sk);
+	struct inet_bind2_bucket *tb2;
 	struct inet_bind_bucket *tb;
 	int l3mdev;
 
 	spin_lock(&head->lock);
+	spin_lock(&head2->lock);
 	tb = inet_csk(sk)->icsk_bind_hash;
-	if (unlikely(!tb)) {
+	tb2 = inet_csk(sk)->icsk_bind2_hash;
+	if (unlikely(!tb || !tb2)) {
+		spin_unlock(&head2->lock);
 		spin_unlock(&head->lock);
 		return -ENOENT;
 	}
@@ -153,25 +239,49 @@  int __inet_inherit_port(const struct sock *sk, struct sock *child)
 		 * as that of the child socket. We have to look up or
 		 * create a new bind bucket for the child here. */
 		inet_bind_bucket_for_each(tb, &head->chain) {
-			if (net_eq(ib_net(tb), sock_net(sk)) &&
-			    tb->l3mdev == l3mdev && tb->port == port)
+			if (inet_bind_bucket_match(tb, net, port, l3mdev))
 				break;
 		}
 		if (!tb) {
 			tb = inet_bind_bucket_create(table->bind_bucket_cachep,
-						     sock_net(sk), head, port,
-						     l3mdev);
+						     net, head, port, l3mdev);
 			if (!tb) {
+				spin_unlock(&head2->lock);
 				spin_unlock(&head->lock);
 				return -ENOMEM;
 			}
+			created_inet_bind_bucket = true;
+		}
+		update_fastreuse = true;
+
+		goto bhash2_find;
+	} else if (!inet_bind2_bucket_addr_match(tb2, child)) {
+		l3mdev = inet_sk_bound_l3mdev(sk);
+
+bhash2_find:
+		tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, child);
+		if (!tb2) {
+			tb2 = inet_bind2_bucket_create(table->bind2_bucket_cachep,
+						       net, head2, port,
+						       l3mdev, child);
+			if (!tb2)
+				goto error;
 		}
-		inet_csk_update_fastreuse(tb, child);
 	}
-	inet_bind_hash(child, tb, port);
+	if (update_fastreuse)
+		inet_csk_update_fastreuse(tb, child);
+	inet_bind_hash(child, tb, tb2, port);
+	spin_unlock(&head2->lock);
 	spin_unlock(&head->lock);
 
 	return 0;
+
+error:
+	if (created_inet_bind_bucket)
+		inet_bind_bucket_destroy(table->bind_bucket_cachep, tb);
+	spin_unlock(&head2->lock);
+	spin_unlock(&head->lock);
+	return -ENOMEM;
 }
 EXPORT_SYMBOL_GPL(__inet_inherit_port);
 
@@ -675,6 +785,112 @@  void inet_unhash(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(inet_unhash);
 
+static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb,
+				    const struct net *net, unsigned short port,
+				    int l3mdev, const struct sock *sk)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == AF_INET6)
+		return net_eq(ib2_net(tb), net) && tb->port == port &&
+			tb->l3mdev == l3mdev &&
+			ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr);
+	else
+#endif
+		return net_eq(ib2_net(tb), net) && tb->port == port &&
+			tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr;
+}
+
+bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net,
+				      unsigned short port, int l3mdev, const struct sock *sk)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	struct in6_addr addr_any = {};
+
+	if (sk->sk_family == AF_INET6)
+		return net_eq(ib2_net(tb), net) && tb->port == port &&
+			tb->l3mdev == l3mdev &&
+			ipv6_addr_equal(&tb->v6_rcv_saddr, &addr_any);
+	else
+#endif
+		return net_eq(ib2_net(tb), net) && tb->port == port &&
+			tb->l3mdev == l3mdev && tb->rcv_saddr == 0;
+}
+
+/* The socket's bhash2 hashbucket spinlock must be held when this is called */
+struct inet_bind2_bucket *
+inet_bind2_bucket_find(const struct inet_bind_hashbucket *head, const struct net *net,
+		       unsigned short port, int l3mdev, const struct sock *sk)
+{
+	struct inet_bind2_bucket *bhash2 = NULL;
+
+	inet_bind_bucket_for_each(bhash2, &head->chain)
+		if (inet_bind2_bucket_match(bhash2, net, port, l3mdev, sk))
+			break;
+
+	return bhash2;
+}
+
+struct inet_bind_hashbucket *
+inet_bhash2_addr_any_hashbucket(const struct sock *sk, const struct net *net, int port)
+{
+	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
+	u32 hash;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct in6_addr addr_any = {};
+
+	if (sk->sk_family == AF_INET6)
+		hash = ipv6_portaddr_hash(net, &addr_any, port);
+	else
+#endif
+		hash = ipv4_portaddr_hash(net, 0, port);
+
+	return &hinfo->bhash2[hash & (hinfo->bhash_size - 1)];
+}
+
+int inet_bhash2_update_saddr(struct inet_bind_hashbucket *prev_saddr, struct sock *sk)
+{
+	struct inet_hashinfo *hinfo = sk->sk_prot->h.hashinfo;
+	struct inet_bind_hashbucket *head, *head2;
+	struct inet_bind2_bucket *tb2, *new_tb2;
+	int l3mdev = inet_sk_bound_l3mdev(sk);
+	int port = inet_sk(sk)->inet_num;
+	struct net *net = sock_net(sk);
+
+	/* Allocate a bind2 bucket ahead of time to avoid permanently putting
+	 * the bhash2 table in an inconsistent state if a new tb2 bucket
+	 * allocation fails.
+	 */
+	new_tb2 = kmem_cache_alloc(hinfo->bind2_bucket_cachep, GFP_ATOMIC);
+	if (!new_tb2)
+		return -ENOMEM;
+
+	head = &hinfo->bhash[inet_bhashfn(net, port,
+					  hinfo->bhash_size)];
+	head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
+
+	spin_lock_bh(&prev_saddr->lock);
+	__sk_del_bind2_node(sk);
+	inet_bind2_bucket_destroy(hinfo->bind2_bucket_cachep,
+				  inet_csk(sk)->icsk_bind2_hash);
+	spin_unlock_bh(&prev_saddr->lock);
+
+	spin_lock_bh(&head2->lock);
+	tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, sk);
+	if (!tb2) {
+		tb2 = new_tb2;
+		inet_bind2_bucket_init(tb2, net, head2, port, l3mdev, sk);
+	}
+	sk_add_bind2_node(sk, &tb2->owners);
+	inet_csk(sk)->icsk_bind2_hash = tb2;
+	spin_unlock_bh(&head2->lock);
+
+	if (tb2 != new_tb2)
+		kmem_cache_free(hinfo->bind2_bucket_cachep, new_tb2);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(inet_bhash2_update_saddr);
+
 /* RFC 6056 3.3.4.  Algorithm 4: Double-Hash Port Selection Algorithm
  * Note that we use 32bit integers (vs RFC 'short integers')
  * because 2^16 is not a multiple of num_ephemeral and this
@@ -689,16 +905,19 @@  EXPORT_SYMBOL_GPL(inet_unhash);
 static u32 *table_perturb;
 
 int __inet_hash_connect(struct inet_timewait_death_row *death_row,
-		struct sock *sk, u64 port_offset,
-		int (*check_established)(struct inet_timewait_death_row *,
-			struct sock *, __u16, struct inet_timewait_sock **))
+			struct sock *sk, u64 port_offset,
+			int (*check_established)(struct inet_timewait_death_row *,
+						 struct sock *, __u16,
+						 struct inet_timewait_sock **))
 {
 	struct inet_hashinfo *hinfo = death_row->hashinfo;
+	struct inet_bind_hashbucket *head, *head2;
 	struct inet_timewait_sock *tw = NULL;
-	struct inet_bind_hashbucket *head;
 	int port = inet_sk(sk)->inet_num;
 	struct net *net = sock_net(sk);
+	struct inet_bind2_bucket *tb2;
 	struct inet_bind_bucket *tb;
+	bool tb_created = false;
 	u32 remaining, offset;
 	int ret, i, low, high;
 	int l3mdev;
@@ -755,8 +974,7 @@  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
 		 * the established check is already unique enough.
 		 */
 		inet_bind_bucket_for_each(tb, &head->chain) {
-			if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
-			    tb->port == port) {
+			if (inet_bind_bucket_match(tb, net, port, l3mdev)) {
 				if (tb->fastreuse >= 0 ||
 				    tb->fastreuseport >= 0)
 					goto next_port;
@@ -774,6 +992,7 @@  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
 			spin_unlock_bh(&head->lock);
 			return -ENOMEM;
 		}
+		tb_created = true;
 		tb->fastreuse = -1;
 		tb->fastreuseport = -1;
 		goto ok;
@@ -789,6 +1008,20 @@  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
 	return -EADDRNOTAVAIL;
 
 ok:
+	/* Find the corresponding tb2 bucket since we need to
+	 * add the socket to the bhash2 table as well
+	 */
+	head2 = inet_bhashfn_portaddr(hinfo, sk, net, port);
+	spin_lock(&head2->lock);
+
+	tb2 = inet_bind2_bucket_find(head2, net, port, l3mdev, sk);
+	if (!tb2) {
+		tb2 = inet_bind2_bucket_create(hinfo->bind2_bucket_cachep, net,
+					       head2, port, l3mdev, sk);
+		if (!tb2)
+			goto error;
+	}
+
 	/* Here we want to add a little bit of randomness to the next source
 	 * port that will be chosen. We use a max() with a random here so that
 	 * on low contention the randomness is maximal and on high contention
@@ -798,7 +1031,10 @@  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
 	WRITE_ONCE(table_perturb[index], READ_ONCE(table_perturb[index]) + i + 2);
 
 	/* Head lock still held and bh's disabled */
-	inet_bind_hash(sk, tb, port);
+	inet_bind_hash(sk, tb, tb2, port);
+
+	spin_unlock(&head2->lock);
+
 	if (sk_unhashed(sk)) {
 		inet_sk(sk)->inet_sport = htons(port);
 		inet_ehash_nolisten(sk, (struct sock *)tw, NULL);
@@ -810,6 +1046,13 @@  int __inet_hash_connect(struct inet_timewait_death_row *death_row,
 		inet_twsk_deschedule_put(tw);
 	local_bh_enable();
 	return 0;
+
+error:
+	spin_unlock(&head2->lock);
+	if (tb_created)
+		inet_bind_bucket_destroy(hinfo->bind_bucket_cachep, tb);
+	spin_unlock_bh(&head->lock);
+	return -ENOMEM;
 }
 
 /*
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 21bdee88383b..a10c162a7f9c 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4677,6 +4677,12 @@  void __init tcp_init(void)
 				  SLAB_HWCACHE_ALIGN | SLAB_PANIC |
 				  SLAB_ACCOUNT,
 				  NULL);
+	tcp_hashinfo.bind2_bucket_cachep =
+		kmem_cache_create("tcp_bind2_bucket",
+				  sizeof(struct inet_bind2_bucket), 0,
+				  SLAB_HWCACHE_ALIGN | SLAB_PANIC |
+				  SLAB_ACCOUNT,
+				  NULL);
 
 	/* Size and allocate the main established and bind bucket
 	 * hash tables.
@@ -4700,7 +4706,7 @@  void __init tcp_init(void)
 		panic("TCP: failed to alloc ehash_locks");
 	tcp_hashinfo.bhash =
 		alloc_large_system_hash("TCP bind",
-					sizeof(struct inet_bind_hashbucket),
+					2 * sizeof(struct inet_bind_hashbucket),
 					tcp_hashinfo.ehash_mask + 1,
 					17, /* one slot per 128 KB of memory */
 					0,
@@ -4709,9 +4715,12 @@  void __init tcp_init(void)
 					0,
 					64 * 1024);
 	tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size;
+	tcp_hashinfo.bhash2 = tcp_hashinfo.bhash + tcp_hashinfo.bhash_size;
 	for (i = 0; i < tcp_hashinfo.bhash_size; i++) {
 		spin_lock_init(&tcp_hashinfo.bhash[i].lock);
 		INIT_HLIST_HEAD(&tcp_hashinfo.bhash[i].chain);
+		spin_lock_init(&tcp_hashinfo.bhash2[i].lock);
+		INIT_HLIST_HEAD(&tcp_hashinfo.bhash2[i].chain);
 	}
 
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 228d36692d08..00c2db4c9f1a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -200,10 +200,11 @@  static int tcp_v4_pre_connect(struct sock *sk, struct sockaddr *uaddr,
 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
+	struct inet_bind_hashbucket *prev_addr_hashbucket = NULL;
+	__be32 daddr, nexthop, prev_sk_rcv_saddr;
 	struct inet_sock *inet = inet_sk(sk);
 	struct tcp_sock *tp = tcp_sk(sk);
 	__be16 orig_sport, orig_dport;
-	__be32 daddr, nexthop;
 	struct flowi4 *fl4;
 	struct rtable *rt;
 	int err;
@@ -246,10 +247,26 @@  int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 	if (!inet_opt || !inet_opt->opt.srr)
 		daddr = fl4->daddr;
 
-	if (!inet->inet_saddr)
+	if (!inet->inet_saddr) {
+		prev_addr_hashbucket = inet_bhashfn_portaddr(&tcp_hashinfo,
+							     sk, sock_net(sk),
+							     inet->inet_num);
+		prev_sk_rcv_saddr = sk->sk_rcv_saddr;
 		inet->inet_saddr = fl4->saddr;
+	}
+
 	sk_rcv_saddr_set(sk, inet->inet_saddr);
 
+	if (prev_addr_hashbucket) {
+		err = inet_bhash2_update_saddr(prev_addr_hashbucket, sk);
+		if (err) {
+			inet->inet_saddr = 0;
+			sk_rcv_saddr_set(sk, prev_sk_rcv_saddr);
+			ip_rt_put(rt);
+			return err;
+		}
+	}
+
 	if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {
 		/* Reset inherited state */
 		tp->rx_opt.ts_recent	   = 0;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 70d4890d8d2f..85337f22ade7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -287,8 +287,20 @@  static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 	}
 
 	if (!saddr) {
+		struct in6_addr prev_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
+		struct inet_bind_hashbucket *prev_addr_hashbucket;
+
+		prev_addr_hashbucket = inet_bhashfn_portaddr(&tcp_hashinfo,
+							     sk, sock_net(sk),
+							     inet->inet_num);
 		saddr = &fl6.saddr;
 		sk->sk_v6_rcv_saddr = *saddr;
+
+		err = inet_bhash2_update_saddr(prev_addr_hashbucket, sk);
+		if (err) {
+			sk->sk_v6_rcv_saddr = prev_v6_rcv_saddr;
+			goto failure;
+		}
 	}
 
 	/* set the source address */