From: Mark Zealey Date: Mon, 2 Dec 2013 09:12:46 +0000 (+0200) Subject: fixes PowerDNS/pdns#666 X-Git-Tag: rec-3.6.0-rc1~239^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4ee30c66374766a29af9364d62bf5b526bd14bd9;p=thirdparty%2Fpdns.git fixes PowerDNS/pdns#666 SO_REUSEPORT is available on various bsd operating systems as standard, and also as a linux kernel patch from Google. It allows 1) Running 2 powerdns processes concurrently so that you can restart powerdns without loosing any packets 2) (the main purpose for my writing this patch) On linux with a patched kernel removes contention from many threads using a socket. In my tests this improves performance with a packet cache from 300kqps to 1mqps If the SO_REUSEPORT call is available, the attached patch causes each receiver thread to open a new socket for connections which allows each thread (on linux) to operate at full speed rather than waiting on a slow socket. It should fail nicely ie if the call is not available at either compile time or run time it will just use the initially created socket. Was merged in to linux 3.9 series - see https://lwn.net/Articles/542629/ for more information --- diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index eb75054918..af0bd6b521 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -243,6 +243,13 @@ void *qthread(void *number) bool logDNSQueries = ::arg().mustDo("log-dns-queries"); bool skipfirst=true; unsigned int maintcount = 0; + UDPNameserver *NS = N; + + // If we have SO_REUSEPORT then create a new port for all receiver threads + // other than the first one. + if( number > 0 && NS->canReusePort() ) + NS = new UDPNameserver(); + for(;;) { if (skipfirst) skipfirst=false; @@ -258,7 +265,8 @@ void *qthread(void *number) } } - if(!(P=N->receive(&question))) { // receive a packet inline + + if(!(P=NS->receive(&question))) { // receive a packet inline continue; // packet was broken, try again } @@ -297,7 +305,7 @@ void *qthread(void *number) cached.d.id=P->d.id; cached.commitD(); // commit d to the packet inlined - N->send(&cached); // answer it then inlined + NS->send(&cached); // answer it then inlined diff=P->d_dt.udiff(); avg_latency=(int)(0.999*avg_latency+0.001*diff); // 'EWMA' diff --git a/pdns/nameserver.cc b/pdns/nameserver.cc index c5b0bb0365..0e773fa300 100644 --- a/pdns/nameserver.cc +++ b/pdns/nameserver.cc @@ -94,6 +94,7 @@ void UDPNameserver::bindIPv4() { vectorlocals; stringtok(locals,::arg()["local-address"]," ,"); + int one = 1; if(locals.empty()) throw PDNSException("No local address specified"); @@ -116,10 +117,14 @@ void UDPNameserver::bindIPv4() memset(&locala,0,sizeof(locala)); locala.sin4.sin_family=AF_INET; - if(localname=="0.0.0.0") { - int val=1; - setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val)); - } + if(localname=="0.0.0.0") + setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); + +#ifdef SO_REUSEPORT + if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) ) + d_can_reuseport = false; +#endif + locala=ComboAddress(localname, ::arg().asNum("local-port")); if(locala.sin4.sin_family != AF_INET) throw PDNSException("Attempting to bind IPv4 socket to IPv6 address"); @@ -188,6 +193,7 @@ void UDPNameserver::bindIPv6() #if HAVE_IPV6 vector locals; stringtok(locals,::arg()["local-ipv6"]," ,"); + int one=1; if(locals.empty()) return; @@ -208,11 +214,16 @@ void UDPNameserver::bindIPv6() ComboAddress locala(localname, ::arg().asNum("local-port")); if(IsAnyAddress(locala)) { - int val=1; - setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &val, sizeof(val)); // linux supports this, so why not - might fail on other systems - setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); - setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); // if this fails, we report an error in tcpreceiver too + setsockopt(s, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems + setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); // if this fails, we report an error in tcpreceiver too } + +#ifdef SO_REUSEPORT + if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) ) + d_can_reuseport = false; +#endif + g_localaddresses.push_back(locala); if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) { L< d_sockets; void bindIPv4(); void bindIPv6();