aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2013-01-22 04:50:44 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-23 13:44:01 -0500
commit72289b96c943757220ccc681fe2e22b46e21aced (patch)
tree8769b30ecfc83acceb5c0095e21db371efad8b61 /net/ipv6/udp.c
parent5ba24953e9707387cce87b07f0d5fbdd03c5c11b (diff)
soreuseport: UDP/IPv6 implementation
Motivation for soreuseport would be something like a DNS server.  An alternative would be to recv on the same socket from multiple threads. As in the case of TCP, the load across these threads tends to be disproportionate and we also see a lot of contection on the socket lock. Note that SO_REUSEADDR already allows multiple UDP sockets to bind to the same port, however there is no provision to prevent hijacking and nothing to distribute packets across all the sockets sharing the same bound port.  This patch does not change the semantics of SO_REUSEADDR, but provides usable functionality of it for unicast. Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r--net/ipv6/udp.c30
1 files changed, 27 insertions, 3 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 1afb635d9b57..cb5bf497c09c 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -45,6 +45,7 @@
45#include <net/tcp_states.h> 45#include <net/tcp_states.h>
46#include <net/ip6_checksum.h> 46#include <net/ip6_checksum.h>
47#include <net/xfrm.h> 47#include <net/xfrm.h>
48#include <net/inet6_hashtables.h>
48 49
49#include <linux/proc_fs.h> 50#include <linux/proc_fs.h>
50#include <linux/seq_file.h> 51#include <linux/seq_file.h>
@@ -203,7 +204,8 @@ static struct sock *udp6_lib_lookup2(struct net *net,
203{ 204{
204 struct sock *sk, *result; 205 struct sock *sk, *result;
205 struct hlist_nulls_node *node; 206 struct hlist_nulls_node *node;
206 int score, badness; 207 int score, badness, matches = 0, reuseport = 0;
208 u32 hash = 0;
207 209
208begin: 210begin:
209 result = NULL; 211 result = NULL;
@@ -214,8 +216,18 @@ begin:
214 if (score > badness) { 216 if (score > badness) {
215 result = sk; 217 result = sk;
216 badness = score; 218 badness = score;
217 if (score == SCORE2_MAX) 219 reuseport = sk->sk_reuseport;
220 if (reuseport) {
221 hash = inet6_ehashfn(net, daddr, hnum,
222 saddr, sport);
223 matches = 1;
224 } else if (score == SCORE2_MAX)
218 goto exact_match; 225 goto exact_match;
226 } else if (score == badness && reuseport) {
227 matches++;
228 if (((u64)hash * matches) >> 32 == 0)
229 result = sk;
230 hash = next_pseudo_random32(hash);
219 } 231 }
220 } 232 }
221 /* 233 /*
@@ -249,7 +261,8 @@ struct sock *__udp6_lib_lookup(struct net *net,
249 unsigned short hnum = ntohs(dport); 261 unsigned short hnum = ntohs(dport);
250 unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); 262 unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
251 struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; 263 struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
252 int score, badness; 264 int score, badness, matches = 0, reuseport = 0;
265 u32 hash = 0;
253 266
254 rcu_read_lock(); 267 rcu_read_lock();
255 if (hslot->count > 10) { 268 if (hslot->count > 10) {
@@ -284,6 +297,17 @@ begin:
284 if (score > badness) { 297 if (score > badness) {
285 result = sk; 298 result = sk;
286 badness = score; 299 badness = score;
300 reuseport = sk->sk_reuseport;
301 if (reuseport) {
302 hash = inet6_ehashfn(net, daddr, hnum,
303 saddr, sport);
304 matches = 1;
305 }
306 } else if (score == badness && reuseport) {
307 matches++;
308 if (((u64)hash * matches) >> 32 == 0)
309 result = sk;
310 hash = next_pseudo_random32(hash);
287 } 311 }
288 } 312 }
289 /* 313 /*