Skip to content

Commit 1580ab6

Browse files
edumazetdavem330
authored andcommitted
tcp/dccp: better use of ephemeral ports in connect()
In commit 07f4c90 ("tcp/dccp: try to not exhaust ip_local_port_range in connect()"), I added a very simple heuristic, so that we got better chances to use even ports, and allow bind() users to have more available slots. It gave nice results, but with more than 200,000 TCP sessions on a typical server, the ~30,000 ephemeral ports are still a rare resource. I chose to go a step further, by looking at all even ports, and if none was available, fallback to odd ports. The companion patch does the same in bind(), but in opposite way. I've seen exec times of up to 30ms on busy servers, so I no longer disable BH for the whole traversal, but only for each hash bucket. I also call cond_resched() to be gentle to other tasks. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 3134b9f commit 1580ab6

File tree

1 file changed

+85
-85
lines changed

1 file changed

+85
-85
lines changed

net/ipv4/inet_hashtables.c

Lines changed: 85 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -565,106 +565,106 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
565565
struct sock *, __u16, struct inet_timewait_sock **))
566566
{
567567
struct inet_hashinfo *hinfo = death_row->hashinfo;
568-
const unsigned short snum = inet_sk(sk)->inet_num;
568+
struct inet_timewait_sock *tw = NULL;
569569
struct inet_bind_hashbucket *head;
570-
struct inet_bind_bucket *tb;
571-
int ret;
570+
int port = inet_sk(sk)->inet_num;
572571
struct net *net = sock_net(sk);
572+
struct inet_bind_bucket *tb;
573+
u32 remaining, offset;
574+
int ret, i, low, high;
575+
static u32 hint;
576+
577+
if (port) {
578+
head = &hinfo->bhash[inet_bhashfn(net, port,
579+
hinfo->bhash_size)];
580+
tb = inet_csk(sk)->icsk_bind_hash;
581+
spin_lock_bh(&head->lock);
582+
if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
583+
inet_ehash_nolisten(sk, NULL);
584+
spin_unlock_bh(&head->lock);
585+
return 0;
586+
}
587+
spin_unlock(&head->lock);
588+
/* No definite answer... Walk to established hash table */
589+
ret = check_established(death_row, sk, port, NULL);
590+
local_bh_enable();
591+
return ret;
592+
}
573593

574-
if (!snum) {
575-
int i, remaining, low, high, port;
576-
static u32 hint;
577-
u32 offset = hint + port_offset;
578-
struct inet_timewait_sock *tw = NULL;
594+
inet_get_local_port_range(net, &low, &high);
595+
high++; /* [32768, 60999] -> [32768, 61000[ */
596+
remaining = high - low;
597+
if (likely(remaining > 1))
598+
remaining &= ~1U;
579599

580-
inet_get_local_port_range(net, &low, &high);
581-
remaining = (high - low) + 1;
600+
offset = (hint + port_offset) % remaining;
601+
/* In first pass we try ports of @low parity.
602+
* inet_csk_get_port() does the opposite choice.
603+
*/
604+
offset &= ~1U;
605+
other_parity_scan:
606+
port = low + offset;
607+
for (i = 0; i < remaining; i += 2, port += 2) {
608+
if (unlikely(port >= high))
609+
port -= remaining;
610+
if (inet_is_local_reserved_port(net, port))
611+
continue;
612+
head = &hinfo->bhash[inet_bhashfn(net, port,
613+
hinfo->bhash_size)];
614+
spin_lock_bh(&head->lock);
582615

583-
/* By starting with offset being an even number,
584-
* we tend to leave about 50% of ports for other uses,
585-
* like bind(0).
616+
/* Does not bother with rcv_saddr checks, because
617+
* the established check is already unique enough.
586618
*/
587-
offset &= ~1;
588-
589-
local_bh_disable();
590-
for (i = 0; i < remaining; i++) {
591-
port = low + (i + offset) % remaining;
592-
if (inet_is_local_reserved_port(net, port))
593-
continue;
594-
head = &hinfo->bhash[inet_bhashfn(net, port,
595-
hinfo->bhash_size)];
596-
spin_lock(&head->lock);
597-
598-
/* Does not bother with rcv_saddr checks,
599-
* because the established check is already
600-
* unique enough.
601-
*/
602-
inet_bind_bucket_for_each(tb, &head->chain) {
603-
if (net_eq(ib_net(tb), net) &&
604-
tb->port == port) {
605-
if (tb->fastreuse >= 0 ||
606-
tb->fastreuseport >= 0)
607-
goto next_port;
608-
WARN_ON(hlist_empty(&tb->owners));
609-
if (!check_established(death_row, sk,
610-
port, &tw))
611-
goto ok;
619+
inet_bind_bucket_for_each(tb, &head->chain) {
620+
if (net_eq(ib_net(tb), net) && tb->port == port) {
621+
if (tb->fastreuse >= 0 ||
622+
tb->fastreuseport >= 0)
612623
goto next_port;
613-
}
624+
WARN_ON(hlist_empty(&tb->owners));
625+
if (!check_established(death_row, sk,
626+
port, &tw))
627+
goto ok;
628+
goto next_port;
614629
}
615-
616-
tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
617-
net, head, port);
618-
if (!tb) {
619-
spin_unlock(&head->lock);
620-
break;
621-
}
622-
tb->fastreuse = -1;
623-
tb->fastreuseport = -1;
624-
goto ok;
625-
626-
next_port:
627-
spin_unlock(&head->lock);
628630
}
629-
local_bh_enable();
630-
631-
return -EADDRNOTAVAIL;
632631

633-
ok:
634-
hint += (i + 2) & ~1;
635-
636-
/* Head lock still held and bh's disabled */
637-
inet_bind_hash(sk, tb, port);
638-
if (sk_unhashed(sk)) {
639-
inet_sk(sk)->inet_sport = htons(port);
640-
inet_ehash_nolisten(sk, (struct sock *)tw);
632+
tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
633+
net, head, port);
634+
if (!tb) {
635+
spin_unlock_bh(&head->lock);
636+
return -ENOMEM;
641637
}
642-
if (tw)
643-
inet_twsk_bind_unhash(tw, hinfo);
644-
spin_unlock(&head->lock);
638+
tb->fastreuse = -1;
639+
tb->fastreuseport = -1;
640+
goto ok;
641+
next_port:
642+
spin_unlock_bh(&head->lock);
643+
cond_resched();
644+
}
645645

646-
if (tw)
647-
inet_twsk_deschedule_put(tw);
646+
offset++;
647+
if ((offset & 1) && remaining > 1)
648+
goto other_parity_scan;
648649

649-
ret = 0;
650-
goto out;
651-
}
650+
return -EADDRNOTAVAIL;
652651

653-
head = &hinfo->bhash[inet_bhashfn(net, snum, hinfo->bhash_size)];
654-
tb = inet_csk(sk)->icsk_bind_hash;
655-
spin_lock_bh(&head->lock);
656-
if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
657-
inet_ehash_nolisten(sk, NULL);
658-
spin_unlock_bh(&head->lock);
659-
return 0;
660-
} else {
661-
spin_unlock(&head->lock);
662-
/* No definite answer... Walk to established hash table */
663-
ret = check_established(death_row, sk, snum, NULL);
664-
out:
665-
local_bh_enable();
666-
return ret;
652+
ok:
653+
hint += i + 2;
654+
655+
/* Head lock still held and bh's disabled */
656+
inet_bind_hash(sk, tb, port);
657+
if (sk_unhashed(sk)) {
658+
inet_sk(sk)->inet_sport = htons(port);
659+
inet_ehash_nolisten(sk, (struct sock *)tw);
667660
}
661+
if (tw)
662+
inet_twsk_bind_unhash(tw, hinfo);
663+
spin_unlock(&head->lock);
664+
if (tw)
665+
inet_twsk_deschedule_put(tw);
666+
local_bh_enable();
667+
return 0;
668668
}
669669

670670
/*

0 commit comments

Comments
 (0)