From 2f4d05ecd1fbf6d26071dc601b186ddd51b97e92 Mon Sep 17 00:00:00 2001 From: Daniel Salzman Date: Mon, 18 Jan 2021 11:19:35 +0100 Subject: [PATCH] server: add configuration option for enabling socket affinity --- doc/man/knot.conf.5in | 11 +++++++++++ doc/reference.rst | 15 +++++++++++++++ src/knot/conf/base.c | 6 +++++- src/knot/conf/base.h | 3 ++- src/knot/conf/conf.c | 10 +++++++++- src/knot/conf/conf.h | 20 +++++++++++++++++++- src/knot/conf/schema.c | 3 ++- src/knot/conf/schema.h | 3 ++- src/knot/server/server.c | 22 ++++++++++++++++------ tests/knot/test_confio.c | 4 +++- 10 files changed, 84 insertions(+), 13 deletions(-) diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index c7b838039..3d7f2098e 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -190,6 +190,7 @@ server: tcp\-remote\-io\-timeout: INT tcp\-max\-clients: INT tcp\-reuseport: BOOL + socket\-affinity: BOOL udp\-max\-payload: SIZE udp\-max\-payload\-ipv4: SIZE udp\-max\-payload\-ipv6: SIZE @@ -332,6 +333,16 @@ advisable to use this option on slave servers. Change of this parameter requires restart of the Knot server to take effect. .sp \fIDefault:\fP off +.SS socket\-affinity +.sp +If enabled and if SO_REUSEPORT is available on Linux, all configured network +sockets are bound to UDP and TCP workers in order to increase the networking performance. +This mode isn\(aqt recommended for setups where the number of network card queues +is lower than the number of UDP or TCP workers. +.sp +Change of this parameter requires restart of the Knot server to take effect. +.sp +\fIDefault:\fP off .SS tcp\-max\-clients .sp A maximum number of TCP clients connected in parallel, set this below the file diff --git a/doc/reference.rst b/doc/reference.rst index b4e9f6ba9..9e06d31d2 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -141,6 +141,7 @@ General options related to the server. tcp-remote-io-timeout: INT tcp-max-clients: INT tcp-reuseport: BOOL + socket-affinity: BOOL udp-max-payload: SIZE udp-max-payload-ipv4: SIZE udp-max-payload-ipv6: SIZE @@ -328,6 +329,20 @@ Change of this parameter requires restart of the Knot server to take effect. *Default:* off +.. _server_socket-affinity: + +socket-affinity +--------------- + +If enabled and if SO_REUSEPORT is available on Linux, all configured network +sockets are bound to UDP and TCP workers in order to increase the networking performance. +This mode isn't recommended for setups where the number of network card queues +is lower than the number of UDP or TCP workers. + +Change of this parameter requires restart of the Knot server to take effect. + +*Default:* off + .. _server_tcp-max-clients: tcp-max-clients diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c index edb40c02c..279e0df06 100644 --- a/src/knot/conf/base.c +++ b/src/knot/conf/base.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -124,6 +124,7 @@ static void init_cache( static bool first_init = true; static bool running_tcp_reuseport; + static bool running_socket_affinity; static size_t running_udp_threads; static size_t running_tcp_threads; static size_t running_xdp_threads; @@ -131,6 +132,7 @@ static void init_cache( if (first_init || reinit_cache) { running_tcp_reuseport = conf_tcp_reuseport(conf); + running_socket_affinity = conf_socket_affinity(conf); running_udp_threads = conf_udp_threads(conf); running_tcp_threads = conf_tcp_threads(conf); running_xdp_threads = conf_xdp_threads(conf); @@ -183,6 +185,8 @@ static void init_cache( conf->cache.srv_tcp_reuseport = running_tcp_reuseport; + conf->cache.srv_socket_affinity = running_socket_affinity; + conf->cache.srv_udp_threads = running_udp_threads; conf->cache.srv_tcp_threads = running_tcp_threads; diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h index 464f03c09..53f97f2c8 100644 --- a/src/knot/conf/base.h +++ b/src/knot/conf/base.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -114,6 +114,7 @@ typedef struct { int srv_tcp_io_timeout; int srv_tcp_remote_io_timeout; bool srv_tcp_reuseport; + bool srv_socket_affinity; size_t srv_udp_threads; size_t srv_tcp_threads; size_t srv_xdp_threads; diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 1eba3acf1..ebd1b050c 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1174,6 +1174,14 @@ bool conf_tcp_reuseport_txn( return conf_bool(&val); } +bool conf_socket_affinity_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_SOCKET_AFFINITY); + return conf_bool(&val); +} + size_t conf_udp_threads_txn( conf_t *conf, knot_db_txn_t *txn) diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index 84c90660e..e3d01d540 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -686,6 +686,24 @@ static inline bool conf_tcp_reuseport( return conf_tcp_reuseport_txn(conf, &conf->read_txn); } +/*! + * Gets the configured setting of the socket affinity switch. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return True if enabled, false otherwise. + */ +bool conf_socket_affinity_txn( + conf_t *conf, + knot_db_txn_t *txn +); +static inline bool conf_socket_affinity( + conf_t *conf) +{ + return conf_socket_affinity_txn(conf, &conf->read_txn); +} + /*! * Gets the configured number of UDP threads. * diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index a3eb18aea..d39fd1590 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -171,6 +171,7 @@ static const yp_item_t desc_server[] = { { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5000 } }, { C_TCP_MAX_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, YP_NIL } }, { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE }, + { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE }, { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD, KNOT_EDNS_MAX_UDP_PAYLOAD, 1232, YP_SSIZE } }, diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h index 931ad1201..0039962c9 100644 --- a/src/knot/conf/schema.h +++ b/src/knot/conf/schema.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,6 +105,7 @@ #define C_SERVER "\x06""server" #define C_SIGNING_THREADS "\x0F""signing-threads" #define C_SINGLE_TYPE_SIGNING "\x13""single-type-signing" +#define C_SOCKET_AFFINITY "\x0F""socket-affinity" #define C_SRV "\x06""server" #define C_STATS "\x0A""statistics" #define C_STORAGE "\x07""storage" diff --git a/src/knot/server/server.c b/src/knot/server/server.c index ca42135d1..b4bf25d97 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -307,13 +307,14 @@ static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, unsigned *t * \param udp_thread_count Number of created UDP workers. * \param tcp_thread_count Number of created TCP workers. * \param tcp_reuseport Indication if reuseport on TCP is enabled. + * \param socket_affinity Indication if CBPF should be attached. * * \retval Pointer to a new initialized inteface. * \retval NULL if error. */ static iface_t *server_init_iface(struct sockaddr_storage *addr, int udp_thread_count, int tcp_thread_count, - bool tcp_reuseport) + bool tcp_reuseport, bool socket_affinity) { iface_t *new_if = calloc(1, sizeof(*new_if)); if (new_if == NULL) { @@ -374,7 +375,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, return NULL; } - if (udp_bind_flags & NET_BIND_MULTIPLE) { + if ((udp_bind_flags & NET_BIND_MULTIPLE) && socket_affinity) { if (!server_attach_reuseport_bpf(sock, udp_socket_count) && warn_cbpf) { log_warning("cannot ensure optimal CPU locality for UDP"); @@ -446,7 +447,7 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, return NULL; } - if (tcp_bind_flags & NET_BIND_MULTIPLE) { + if ((tcp_bind_flags & NET_BIND_MULTIPLE) && socket_affinity) { if (!server_attach_reuseport_bpf(sock, tcp_socket_count) && warn_cbpf) { log_warning("cannot ensure optimal CPU locality for TCP"); @@ -475,7 +476,9 @@ static int configure_sockets(conf_t *conf, server_t *s) #ifdef ENABLE_REUSEPORT /* Log info if reuseport is used and for what protocols. */ - log_info("using reuseport for UDP%s", conf->cache.srv_tcp_reuseport ? " and TCP" : ""); + log_info("using reuseport%s for UDP%s", + conf->cache.srv_socket_affinity ? " with socket affinity" : "", + conf->cache.srv_tcp_reuseport ? " and TCP" : ""); #endif /* Update bound interfaces. */ @@ -505,6 +508,7 @@ static int configure_sockets(conf_t *conf, server_t *s) unsigned size_udp = s->handlers[IO_UDP].handler.unit->size; unsigned size_tcp = s->handlers[IO_TCP].handler.unit->size; bool tcp_reuseport = conf->cache.srv_tcp_reuseport; + bool socket_affinity = conf->cache.srv_socket_affinity; char *rundir = conf_abs_path(&rundir_val, NULL); while (listen_val.code == KNOT_EOK) { struct sockaddr_storage addr = conf_addr(&listen_val, rundir); @@ -513,7 +517,7 @@ static int configure_sockets(conf_t *conf, server_t *s) log_info("binding to interface %s", addr_str); iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, - tcp_reuseport); + tcp_reuseport, socket_affinity); if (new_if == NULL) { server_deinit_iface_list(newlist, nifs); free(rundir); @@ -861,6 +865,7 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) const char *msg = "changes of %s require restart to take effect"; static bool warn_tcp_reuseport = true; + static bool warn_socket_affinity = true; static bool warn_udp = true; static bool warn_tcp = true; static bool warn_bg = true; @@ -871,6 +876,11 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) warn_tcp_reuseport = false; } + if (warn_socket_affinity && conf->cache.srv_socket_affinity != conf_socket_affinity(conf)) { + log_warning(msg, &C_SOCKET_AFFINITY[1]); + warn_socket_affinity = false; + } + if (warn_udp && server->handlers[IO_UDP].size != conf_udp_threads(conf)) { log_warning(msg, &C_UDP_WORKERS[1]); warn_udp = false; diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c index 191c16162..7945d3374 100644 --- a/tests/knot/test_confio.c +++ b/tests/knot/test_confio.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -908,6 +908,7 @@ static void test_conf_io_list(void) "server.tcp-remote-io-timeout\n" "server.tcp-max-clients\n" "server.tcp-reuseport\n" + "server.socket-affinity\n" "server.udp-workers\n" "server.tcp-workers\n" "server.background-workers\n" @@ -930,6 +931,7 @@ static const yp_item_t desc_server[] = { { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VNONE }, { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE }, { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE }, + { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE }, { C_UDP_WORKERS, YP_TINT, YP_VNONE }, { C_TCP_WORKERS, YP_TINT, YP_VNONE }, { C_BG_WORKERS, YP_TINT, YP_VNONE },