|
@@ -93,24 +93,40 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
|
|
struct inet_bind_hashbucket *head;
|
|
|
struct hlist_node *node;
|
|
|
struct inet_bind_bucket *tb;
|
|
|
- int ret;
|
|
|
+ int ret, attempts = 5;
|
|
|
struct net *net = sock_net(sk);
|
|
|
+ int smallest_size = -1, smallest_rover;
|
|
|
|
|
|
local_bh_disable();
|
|
|
if (!snum) {
|
|
|
int remaining, rover, low, high;
|
|
|
|
|
|
+again:
|
|
|
inet_get_local_port_range(&low, &high);
|
|
|
remaining = (high - low) + 1;
|
|
|
- rover = net_random() % remaining + low;
|
|
|
+ smallest_rover = rover = net_random() % remaining + low;
|
|
|
|
|
|
+ smallest_size = -1;
|
|
|
do {
|
|
|
head = &hashinfo->bhash[inet_bhashfn(net, rover,
|
|
|
hashinfo->bhash_size)];
|
|
|
spin_lock(&head->lock);
|
|
|
inet_bind_bucket_for_each(tb, node, &head->chain)
|
|
|
- if (ib_net(tb) == net && tb->port == rover)
|
|
|
+ if (ib_net(tb) == net && tb->port == rover) {
|
|
|
+ if (tb->fastreuse > 0 &&
|
|
|
+ sk->sk_reuse &&
|
|
|
+ sk->sk_state != TCP_LISTEN &&
|
|
|
+ (tb->num_owners < smallest_size || smallest_size == -1)) {
|
|
|
+ smallest_size = tb->num_owners;
|
|
|
+ smallest_rover = rover;
|
|
|
+ if (hashinfo->bsockets > (high - low) + 1) {
|
|
|
+ spin_unlock(&head->lock);
|
|
|
+ snum = smallest_rover;
|
|
|
+ goto have_snum;
|
|
|
+ }
|
|
|
+ }
|
|
|
goto next;
|
|
|
+ }
|
|
|
break;
|
|
|
next:
|
|
|
spin_unlock(&head->lock);
|
|
@@ -125,14 +141,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
|
|
* the top level, not from the 'break;' statement.
|
|
|
*/
|
|
|
ret = 1;
|
|
|
- if (remaining <= 0)
|
|
|
+ if (remaining <= 0) {
|
|
|
+ if (smallest_size != -1) {
|
|
|
+ snum = smallest_rover;
|
|
|
+ goto have_snum;
|
|
|
+ }
|
|
|
goto fail;
|
|
|
-
|
|
|
+ }
|
|
|
/* OK, here is the one we will use. HEAD is
|
|
|
* non-NULL and we hold it's mutex.
|
|
|
*/
|
|
|
snum = rover;
|
|
|
} else {
|
|
|
+have_snum:
|
|
|
head = &hashinfo->bhash[inet_bhashfn(net, snum,
|
|
|
hashinfo->bhash_size)];
|
|
|
spin_lock(&head->lock);
|
|
@@ -145,12 +166,18 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
|
|
|
tb_found:
|
|
|
if (!hlist_empty(&tb->owners)) {
|
|
|
if (tb->fastreuse > 0 &&
|
|
|
- sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
|
|
|
+ sk->sk_reuse && sk->sk_state != TCP_LISTEN &&
|
|
|
+ smallest_size == -1) {
|
|
|
goto success;
|
|
|
} else {
|
|
|
ret = 1;
|
|
|
- if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb))
|
|
|
+ if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) {
|
|
|
+ if (sk->sk_reuse && sk->sk_state != TCP_LISTEN && --attempts >= 0) {
|
|
|
+ spin_unlock(&head->lock);
|
|
|
+ goto again;
|
|
|
+ }
|
|
|
goto fail_unlock;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
tb_not_found:
|