interface-interval 60;\n\
# keep-response-order {none;};\n\
listen-on {any;};\n\
- listen-on-v6 {any;};\n\
+ listen-on-v6 {any;};\n"
+#if HAVE_SO_REUSEPORT_LB
+ "\
+ load-balance-sockets yes;\n"
+#else
+ "\
+ load-balance-sockets no;\n"
+#endif
+ "\
# lock-file \"" NAMED_LOCALSTATEDIR "/run/named/named.lock\";\n\
match-mapped-addresses no;\n\
max-ixfr-ratio unlimited;\n\
integer ] {
address_match_element; ... };
lmdb-mapsize sizeval;
+ load-balance-sockets boolean;
lock-file ( quoted_string | none );
managed-keys-directory quoted_string;
masterfile-format ( map | raw | text );
uint32_t softquota = 0;
uint32_t max;
uint64_t initial, idle, keepalive, advertised;
+ bool loadbalancesockets;
dns_aclenv_t *env =
ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
}
ns_interfacemgr_setbacklog(server->interfacemgr, backlog);
+ obj = NULL;
+ result = named_config_get(maps, "load-balance-sockets", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ loadbalancesockets = cfg_obj_asboolean(obj);
+#if HAVE_SO_REUSEPORT_LB
+ if (first_time) {
+ isc_nm_setloadbalancesockets(named_g_netmgr,
+ cfg_obj_asboolean(obj));
+ } else if (loadbalancesockets !=
+ isc_nm_getloadbalancesockets(named_g_netmgr)) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING,
+ "changing load-balance-sockets value requires "
+ "server restart");
+ }
+#else
+ if (loadbalancesockets) {
+ cfg_obj_log(
+ obj, named_g_lctx, ISC_LOG_WARNING,
+ "load-balance-sockets has no effect on this system");
+ }
+#endif
+
/*
* Configure the interface manager according to the "listen-on"
* statement.
If ``yes``, respond to root key sentinel probes as described in
draft-ietf-dnsop-kskroll-sentinel-08. The default is ``yes``.
+``load-balance-sockets``
+
+ This option enables kernel load-balancing of sockets on systems which support
+ it, including Linux and FreeBSD. This instructs the kernel to distribute
+ incoming socket connections among the networking threads based on a hashing
+ scheme. For more information, see the receive network flow classification
+ options (``rx-flow-hash``) section in the ``ethtool`` manual page. The
+ default is ``yes``.
+
+ Enabling ``load-balance-sockets`` significantly increases general throughput
+ when incoming traffic is distributed uniformly onto the threads by the
+ operating system. However, in cases where a worker thread is busy with a
+ long-lasting operation, such as processing a Response Policy Zone (RPZ) or
+ Catalog Zone update or an unusually large zone transfer, incoming traffic
+ that hashes onto that thread may be delayed. On servers where these events
+ occur frequently, it may be preferable to disable socket load-balancing so
+ that other threads can pick up the traffic that would have been sent to the
+ busy thread.
+
+ Note: this option can only be set when ``named`` first starts.
+ Changes will not take effect during reconfiguration; the server
+ must be restarted.
+
``message-compression``
If ``yes``, DNS name compression is used in responses to regular
queries (not including AXFR or IXFR, which always use compression).
integer ] {
address_match_element; ... };
lmdb\-mapsize sizeval;
+ load\-balance\-sockets boolean;
lock\-file ( quoted_string | none );
managed\-keys\-directory quoted_string;
masterfile\-format ( map | raw | text );
<integer> ] {
<address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>;
+ load-balance-sockets <boolean>;
lock-file ( <quoted_string> | none );
maintain-ixfr-base <boolean>; // ancient
managed-keys-directory <quoted_string>;
<integer> ] {
<address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>;
+ load-balance-sockets <boolean>;
lock-file ( <quoted_string> | none );
managed-keys-directory <quoted_string>;
masterfile-format ( map | raw | text );
<integer> ] {
<address_match_element>; ... };
lmdb-mapsize <sizeval>;
+ load-balance-sockets <boolean>;
lock-file ( <quoted_string> | none );
managed-keys-directory <quoted_string>;
masterfile-format ( map | raw | text );
* \li 'mgr' is a valid netmgr.
*/
+bool
+isc_nm_getloadbalancesockets(isc_nm_t *mgr);
+void
+isc_nm_setloadbalancesockets(isc_nm_t *mgr, bool enabled);
+/*%<
+ * Get and set value of load balancing of the sockets.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
void
isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
uint32_t *keepalive, uint32_t *advertised);
atomic_uint_fast32_t workers_paused;
atomic_uint_fast32_t maxudp;
+ bool load_balance_sockets;
+
atomic_bool paused;
/*
atomic_init(&mgr->workers_paused, 0);
atomic_init(&mgr->paused, false);
atomic_init(&mgr->closing, false);
+#if HAVE_SO_REUSEPORT_LB
+ mgr->load_balance_sockets = true;
+#else
+ mgr->load_balance_sockets = false;
+#endif
#ifdef NETMGR_TRACE
ISC_LIST_INIT(mgr->active_sockets);
atomic_store(&mgr->advertised, advertised);
}
+void
+isc_nm_setloadbalancesockets(isc_nm_t *mgr, bool enabled) {
+ REQUIRE(VALID_NM(mgr));
+
+#if HAVE_SO_REUSEPORT_LB
+ mgr->load_balance_sockets = enabled;
+#else
+ UNUSED(enabled);
+#endif
+}
+
void
isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
uint32_t *keepalive, uint32_t *advertised) {
}
static uv_os_sock_t
-isc__nm_tcp_lb_socket(sa_family_t sa_family) {
+isc__nm_tcp_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
isc_result_t result;
uv_os_sock_t sock;
result = isc__nm_socket_reuse(sock);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#if HAVE_SO_REUSEPORT_LB
- result = isc__nm_socket_reuse_lb(sock);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#endif
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
return (sock);
}
csock->pquota = sock->pquota;
isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- UNUSED(fd);
- csock->fd = isc__nm_tcp_lb_socket(iface->type.sa.sa_family);
-#else
- csock->fd = dup(fd);
-#endif
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_tcp_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
REQUIRE(csock->fd >= 0);
ievent = isc__nm_get_netievent_tcplisten(mgr, csock);
sock->tid = 0;
sock->fd = -1;
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- fd = isc__nm_tcp_lb_socket(iface->type.sa.sa_family);
-#endif
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_tcp_lb_socket(mgr, iface->type.sa.sa_family);
+ }
isc_barrier_init(&sock->startlistening, sock->nchildren);
start_tcp_child(mgr, iface, sock, fd, isc_nm_tid());
}
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- isc__nm_closesocket(fd);
-#endif
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
LOCK(&sock->lock);
while (atomic_load(&sock->rchildren) != sock->nchildren) {
int flags = 0;
isc_nmsocket_t *sock = NULL;
isc_result_t result;
+ isc_nm_t *mgr;
REQUIRE(VALID_NMSOCK(ievent->sock));
REQUIRE(ievent->sock->tid == isc_nm_tid());
sock = ievent->sock;
sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
REQUIRE(sock->type == isc_nm_tcpsocket);
REQUIRE(sock->parent != NULL);
flags = UV_TCP_IPV6ONLY;
}
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- r = isc_uv_tcp_freebind(&sock->uv_handle.tcp, &sock->iface.type.sa,
- flags);
- if (r < 0) {
- isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
- goto done;
- }
-#else
- if (sock->parent->fd == -1) {
+ if (mgr->load_balance_sockets) {
r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
&sock->iface.type.sa, flags);
if (r < 0) {
sock->statsindex[STATID_BINDFAIL]);
goto done;
}
- sock->parent->uv_handle.tcp.flags = sock->uv_handle.tcp.flags;
- sock->parent->fd = sock->fd;
} else {
- /* The socket is already bound, just copy the flags */
- sock->uv_handle.tcp.flags = sock->parent->uv_handle.tcp.flags;
+ if (sock->parent->fd == -1) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock->mgr, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.tcp.flags =
+ sock->uv_handle.tcp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.tcp.flags =
+ sock->parent->uv_handle.tcp.flags;
+ }
}
-#endif
/*
* The callback will run in the same thread uv_listen() was called
}
static uv_os_sock_t
-isc__nm_tcpdns_lb_socket(sa_family_t sa_family) {
+isc__nm_tcpdns_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
isc_result_t result;
uv_os_sock_t sock;
result = isc__nm_socket_reuse(sock);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#if HAVE_SO_REUSEPORT_LB
- result = isc__nm_socket_reuse_lb(sock);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#endif
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
return (sock);
}
csock->pquota = sock->pquota;
isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- UNUSED(fd);
- csock->fd = isc__nm_tcpdns_lb_socket(iface->type.sa.sa_family);
-#else
- csock->fd = dup(fd);
-#endif
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_tcpdns_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
REQUIRE(csock->fd >= 0);
ievent = isc__nm_get_netievent_tcpdnslisten(mgr, csock);
sock->tid = 0;
sock->fd = -1;
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- fd = isc__nm_tcpdns_lb_socket(iface->type.sa.sa_family);
-#endif
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_tcpdns_lb_socket(mgr, iface->type.sa.sa_family);
+ }
isc_barrier_init(&sock->startlistening, sock->nchildren);
start_tcpdns_child(mgr, iface, sock, fd, isc_nm_tid());
}
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- isc__nm_closesocket(fd);
-#endif
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
LOCK(&sock->lock);
while (atomic_load(&sock->rchildren) != sock->nchildren) {
int flags = 0;
isc_nmsocket_t *sock = NULL;
isc_result_t result = ISC_R_UNSET;
+ isc_nm_t *mgr = NULL;
REQUIRE(VALID_NMSOCK(ievent->sock));
REQUIRE(ievent->sock->tid == isc_nm_tid());
sock = ievent->sock;
sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
REQUIRE(sock->type == isc_nm_tcpdnssocket);
REQUIRE(sock->parent != NULL);
flags = UV_TCP_IPV6ONLY;
}
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- r = isc_uv_tcp_freebind(&sock->uv_handle.tcp, &sock->iface.type.sa,
- flags);
- if (r < 0) {
- isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
- goto done;
- }
-#else
- if (sock->parent->fd == -1) {
+ if (mgr->load_balance_sockets) {
r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
&sock->iface.type.sa, flags);
if (r < 0) {
sock->statsindex[STATID_BINDFAIL]);
goto done;
}
- sock->parent->uv_handle.tcp.flags = sock->uv_handle.tcp.flags;
- sock->parent->fd = sock->fd;
} else {
- /* The socket is already bound, just copy the flags */
- sock->uv_handle.tcp.flags = sock->parent->uv_handle.tcp.flags;
+ if (sock->parent->fd == -1) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock->mgr, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.tcp.flags =
+ sock->uv_handle.tcp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.tcp.flags =
+ sock->parent->uv_handle.tcp.flags;
+ }
}
-#endif
/*
* The callback will run in the same thread uv_listen() was called
stop_udp_child(isc_nmsocket_t *sock);
static uv_os_sock_t
-isc__nm_udp_lb_socket(sa_family_t sa_family) {
+isc__nm_udp_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
isc_result_t result;
uv_os_sock_t sock;
result = isc__nm_socket_reuse(sock);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#if HAVE_SO_REUSEPORT_LB
- result = isc__nm_socket_reuse_lb(sock);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
-#endif
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
return (sock);
}
csock->extrahandlesize = sock->extrahandlesize;
csock->tid = tid;
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- UNUSED(fd);
- csock->fd = isc__nm_udp_lb_socket(iface->type.sa.sa_family);
-#else
- csock->fd = dup(fd);
-#endif
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_udp_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
REQUIRE(csock->fd >= 0);
ievent = isc__nm_get_netievent_udplisten(mgr, csock);
sock->tid = 0;
sock->fd = -1;
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- fd = isc__nm_udp_lb_socket(iface->type.sa.sa_family);
-#endif
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_udp_lb_socket(mgr, iface->type.sa.sa_family);
+ }
isc_barrier_init(&sock->startlistening, sock->nchildren);
start_udp_child(mgr, iface, sock, fd, isc_nm_tid());
}
-#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
- isc__nm_closesocket(fd);
-#endif
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
LOCK(&sock->lock);
while (atomic_load(&sock->rchildren) != sock->nchildren) {
int uv_init_flags = 0;
sa_family_t sa_family;
isc_result_t result = ISC_R_UNSET;
+ isc_nm_t *mgr = NULL;
REQUIRE(VALID_NMSOCK(ievent->sock));
REQUIRE(ievent->sock->tid == isc_nm_tid());
sock = ievent->sock;
sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
REQUIRE(sock->type == isc_nm_udpsocket);
REQUIRE(sock->parent != NULL);
uv_bind_flags |= UV_UDP_IPV6ONLY;
}
-#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
- r = isc_uv_udp_freebind(&sock->uv_handle.udp,
- &sock->parent->iface.type.sa, uv_bind_flags);
- if (r < 0) {
- isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
- goto done;
- }
-#else
- if (sock->parent->fd == -1) {
- /* This thread is first, bind the socket */
+ if (mgr->load_balance_sockets) {
r = isc_uv_udp_freebind(&sock->uv_handle.udp,
&sock->parent->iface.type.sa,
uv_bind_flags);
sock->statsindex[STATID_BINDFAIL]);
goto done;
}
- sock->parent->uv_handle.udp.flags = sock->uv_handle.udp.flags;
- sock->parent->fd = sock->fd;
} else {
- /* The socket is already bound, just copy the flags */
- sock->uv_handle.udp.flags = sock->parent->uv_handle.udp.flags;
+ if (sock->parent->fd == -1) {
+ /* This thread is first, bind the socket */
+ r = isc_uv_udp_freebind(&sock->uv_handle.udp,
+ &sock->parent->iface.type.sa,
+ uv_bind_flags);
+ if (r < 0) {
+ isc__nm_incstats(sock->mgr, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.udp.flags =
+ sock->uv_handle.udp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.udp.flags =
+ sock->parent->uv_handle.udp.flags;
+ }
}
-#endif
#ifdef ISC_RECV_BUFFER_SIZE
uv_recv_buffer_size(&sock->uv_handle.handle,
isc_nm_resume
isc_nm_resumeread
isc_nm_send
+isc_nm_setloadbalancesockets
isc_nm_setstats
isc_nm_settimeouts
isc_nm_stoplistening
{ "keep-response-order", &cfg_type_bracketed_aml, 0 },
{ "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
{ "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
+ { "load-balance-sockets", &cfg_type_boolean, 0 },
{ "lock-file", &cfg_type_qstringornone, 0 },
{ "managed-keys-directory", &cfg_type_qstring, 0 },
{ "match-mapped-addresses", &cfg_type_boolean, 0 },