diff options
Diffstat (limited to 'net/ipv4/inet_connection_sock.c')
-rw-r--r-- | net/ipv4/inet_connection_sock.c | 42 |
1 files changed, 35 insertions, 7 deletions
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index f26ab38680de..22cd19ee44e5 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c | |||
@@ -93,24 +93,40 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) | |||
93 | struct inet_bind_hashbucket *head; | 93 | struct inet_bind_hashbucket *head; |
94 | struct hlist_node *node; | 94 | struct hlist_node *node; |
95 | struct inet_bind_bucket *tb; | 95 | struct inet_bind_bucket *tb; |
96 | int ret; | 96 | int ret, attempts = 5; |
97 | struct net *net = sock_net(sk); | 97 | struct net *net = sock_net(sk); |
98 | int smallest_size = -1, smallest_rover; | ||
98 | 99 | ||
99 | local_bh_disable(); | 100 | local_bh_disable(); |
100 | if (!snum) { | 101 | if (!snum) { |
101 | int remaining, rover, low, high; | 102 | int remaining, rover, low, high; |
102 | 103 | ||
104 | again: | ||
103 | inet_get_local_port_range(&low, &high); | 105 | inet_get_local_port_range(&low, &high); |
104 | remaining = (high - low) + 1; | 106 | remaining = (high - low) + 1; |
105 | rover = net_random() % remaining + low; | 107 | smallest_rover = rover = net_random() % remaining + low; |
106 | 108 | ||
109 | smallest_size = -1; | ||
107 | do { | 110 | do { |
108 | head = &hashinfo->bhash[inet_bhashfn(net, rover, | 111 | head = &hashinfo->bhash[inet_bhashfn(net, rover, |
109 | hashinfo->bhash_size)]; | 112 | hashinfo->bhash_size)]; |
110 | spin_lock(&head->lock); | 113 | spin_lock(&head->lock); |
111 | inet_bind_bucket_for_each(tb, node, &head->chain) | 114 | inet_bind_bucket_for_each(tb, node, &head->chain) |
112 | if (ib_net(tb) == net && tb->port == rover) | 115 | if (ib_net(tb) == net && tb->port == rover) { |
116 | if (tb->fastreuse > 0 && | ||
117 | sk->sk_reuse && | ||
118 | sk->sk_state != TCP_LISTEN && | ||
119 | (tb->num_owners < smallest_size || smallest_size == -1)) { | ||
120 | smallest_size = tb->num_owners; | ||
121 | smallest_rover = rover; | ||
122 | if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) { | ||
123 | spin_unlock(&head->lock); | ||
124 | snum = smallest_rover; | ||
125 | goto have_snum; | ||
126 | } | ||
127 | } | ||
113 | goto next; | 128 | goto next; |
129 | } | ||
114 | break; | 130 | break; |
115 | next: | 131 | next: |
116 | spin_unlock(&head->lock); | 132 | spin_unlock(&head->lock); |
@@ -125,14 +141,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) | |||
125 | * the top level, not from the 'break;' statement. | 141 | * the top level, not from the 'break;' statement. |
126 | */ | 142 | */ |
127 | ret = 1; | 143 | ret = 1; |
128 | if (remaining <= 0) | 144 | if (remaining <= 0) { |
145 | if (smallest_size != -1) { | ||
146 | snum = smallest_rover; | ||
147 | goto have_snum; | ||
148 | } | ||
129 | goto fail; | 149 | goto fail; |
130 | 150 | } | |
131 | /* OK, here is the one we will use. HEAD is | 151 | /* OK, here is the one we will use. HEAD is |
132 | * non-NULL and we hold it's mutex. | 152 | * non-NULL and we hold it's mutex. |
133 | */ | 153 | */ |
134 | snum = rover; | 154 | snum = rover; |
135 | } else { | 155 | } else { |
156 | have_snum: | ||
136 | head = &hashinfo->bhash[inet_bhashfn(net, snum, | 157 | head = &hashinfo->bhash[inet_bhashfn(net, snum, |
137 | hashinfo->bhash_size)]; | 158 | hashinfo->bhash_size)]; |
138 | spin_lock(&head->lock); | 159 | spin_lock(&head->lock); |
@@ -145,12 +166,19 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) | |||
145 | tb_found: | 166 | tb_found: |
146 | if (!hlist_empty(&tb->owners)) { | 167 | if (!hlist_empty(&tb->owners)) { |
147 | if (tb->fastreuse > 0 && | 168 | if (tb->fastreuse > 0 && |
148 | sk->sk_reuse && sk->sk_state != TCP_LISTEN) { | 169 | sk->sk_reuse && sk->sk_state != TCP_LISTEN && |
170 | smallest_size == -1) { | ||
149 | goto success; | 171 | goto success; |
150 | } else { | 172 | } else { |
151 | ret = 1; | 173 | ret = 1; |
152 | if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) | 174 | if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) { |
175 | if (sk->sk_reuse && sk->sk_state != TCP_LISTEN && | ||
176 | smallest_size != -1 && --attempts >= 0) { | ||
177 | spin_unlock(&head->lock); | ||
178 | goto again; | ||
179 | } | ||
153 | goto fail_unlock; | 180 | goto fail_unlock; |
181 | } | ||
154 | } | 182 | } |
155 | } | 183 | } |
156 | tb_not_found: | 184 | tb_not_found: |