From: Libor Peltan Date: Wed, 29 Jan 2020 09:58:12 +0000 (+0100) Subject: xdp: listen on configured ifaces with workers=RXqueues X-Git-Tag: embedded_lmdb~35^2~53 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14655e4e3a52b2175f4b2b36ea7593ecd13d20db;p=thirdparty%2Fknot-dns.git xdp: listen on configured ifaces with workers=RXqueues --- diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index c9641839b7..dfabb5b3bd 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -196,6 +196,7 @@ server: edns\-client\-subnet: BOOL answer\-rotation: BOOL listen: ADDR[@INT] ... + listen\-xdp: STR ... .ft P .fi .UNINDENT @@ -268,21 +269,6 @@ over TCP. Change of this parameter requires restart of the Knot server to take effect. .sp \fIDefault:\fP equal to the number of online CPUs, default value is at least 10 -.SS xdp\-workers -.sp -A number of XDP workers (threads) used to process incoming queries -over UDP with the use of eXpress Data Path, bypassing Linux network stack. -.sp -Change of this parameter requires restart of the Knot server to take effect. -.sp -If set to zero, XDP is not initialized at all. -.sp -Pre\-requisite: compiled\-in support for XDP. -.sp -It\(aqs recommended to be set only if the server listens on one interface. -The number of workers should match the number of network interface RX queues. -.sp -\fIDefault:\fP 0 .SS background\-workers .sp A number of workers (threads) used to execute background operations (zone @@ -399,6 +385,23 @@ automatically enabled if supported by the operating system. Change of this parameter requires restart of the Knot server to take effect. .sp \fIDefault:\fP not set +.SS listen\-xdp +.sp +One or more specifications in the form \fBeth_dev@port\fP, e.g. \fBeth1@53\fP\&. +Alternatively, an IP address can be used instead of a device name, but Knot +will still listen on all addresses belonging to the same interface! +.sp +If specified, Knot will create additional XDP workers, listening +on specified interface(s) and port(s) on UDP protocol, with enhanced +performance. The number of XDP workers is equal to the sum of the +interfaces\(aq RX queues. +.sp +It is strongly recommended to also \fI\%listen\fP on all those +addresses, at least to fulfill the requirement of working TCP fallback. +.sp +Change of this parameter requires restart of the Knot server to take effect. +.sp +\fIDefault:\fP not set .SH KEY SECTION .sp Shared TSIG keys used to authenticate communication with the server. diff --git a/doc/operation.rst b/doc/operation.rst index c5d4bbb0cb..9018aabc36 100644 --- a/doc/operation.rst +++ b/doc/operation.rst @@ -968,9 +968,6 @@ Start knotd as root (it may drop to unpriv user after init). Notes ----- -The number of configured :ref:`server_xdp-workers` shall match the number of network -card's queues. - Don't attempt settings like:: # ip link set mtu $mtu dev $dev diff --git a/doc/reference.rst b/doc/reference.rst index 7bc1446531..e5ca159b6f 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -147,6 +147,7 @@ General options related to the server. edns-client-subnet: BOOL answer-rotation: BOOL listen: ADDR[@INT] ... + listen-xdp: STR ... .. CAUTION:: When you change configuration parameters dynamically or via configuration file @@ -244,25 +245,6 @@ Change of this parameter requires restart of the Knot server to take effect. *Default:* equal to the number of online CPUs, default value is at least 10 -.. _server_xdp-workers: - -xdp-workers ------------ - -A number of XDP workers (threads) used to process incoming queries -over UDP with the use of eXpress Data Path, bypassing Linux network stack. - -Change of this parameter requires restart of the Knot server to take effect. - -If set to zero, XDP is not initialized at all. - -Pre-requisite: compiled-in support for XDP. - -It's recommended to be set only if the server listens on one interface. -The number of workers should match the number of network interface RX queues. - -*Default:* 0 - .. _server_background-workers: background-workers @@ -423,6 +405,27 @@ Change of this parameter requires restart of the Knot server to take effect. *Default:* not set +.. _server_listen-xdp: + +listen-xdp +---------- + +One or more specifications in the form ``eth_dev@port``, e.g. ``eth1@53``. +Alternatively, an IP address can be used instead of a device name, but Knot +will still listen on all addresses belonging to the same interface! + +If specified, Knot will create additional XDP workers, listening +on specified interface(s) and port(s) on UDP protocol, with enhanced +performance. The number of XDP workers is equal to the sum of the +interfaces' RX queues. + +It is strongly recommended to also :ref:`server_listen` on all those +addresses, at least to fulfill the requirement of working TCP fallback. + +Change of this parameter requires restart of the Knot server to take effect. + +*Default:* not set + .. _Key section: Key section diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c index 01cc1bfdaf..d724e68d21 100644 --- a/src/knot/conf/base.c +++ b/src/knot/conf/base.c @@ -124,14 +124,12 @@ static void init_cache( static bool first_init = true; static bool running_tcp_reuseport; static size_t running_udp_threads; - static size_t running_xdp_threads; static size_t running_tcp_threads; static size_t running_bg_threads; if (first_init || reinit_cache) { running_tcp_reuseport = conf_tcp_reuseport(conf); running_udp_threads = conf_udp_threads(conf); - running_xdp_threads = conf_xdp_threads(conf); running_tcp_threads = conf_tcp_threads(conf); running_bg_threads = conf_bg_threads(conf); @@ -184,8 +182,6 @@ static void init_cache( conf->cache.srv_udp_threads = running_udp_threads; - conf->cache.srv_xdp_threads = running_xdp_threads; - conf->cache.srv_tcp_threads = running_tcp_threads; conf->cache.srv_bg_threads = running_bg_threads; diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index dbd786e18b..1f289529cf 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -1114,19 +1114,6 @@ size_t conf_udp_threads_txn( return workers; } -size_t conf_xdp_threads_txn( - conf_t *conf, - knot_db_txn_t *txn) -{ - conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_XDP_WORKERS); - int64_t workers = conf_int(&val); - if (workers == YP_NIL) { - return 0; - } - - return workers; -} - size_t conf_tcp_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 36adb67985..a44e879684 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -677,24 +677,6 @@ static inline size_t conf_udp_threads( return conf_udp_threads_txn(conf, &conf->read_txn); } -/*! - * Gets the configured number of XDP threads. - * - * \param[in] conf Configuration. - * \param[in] txn Configuration DB transaction. - * - * \return Number of threads. - */ -size_t conf_xdp_threads_txn( - conf_t *conf, - knot_db_txn_t *txn -); -static inline size_t conf_xdp_threads( - conf_t *conf) -{ - return conf_xdp_threads_txn(conf, &conf->read_txn); -} - /*! * Gets the configured number of TCP threads. * diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index 2d3acb9fca..03bc54653a 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -155,7 +155,6 @@ static const yp_item_t desc_server[] = { { C_USER, YP_TSTR, YP_VNONE }, { C_PIDFILE, YP_TSTR, YP_VSTR = { "knot.pid" } }, { C_UDP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, - { C_XDP_WORKERS, YP_TINT, YP_VINT = { 0, 255, YP_NIL } }, { C_TCP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, { C_BG_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, { C_ASYNC_START, YP_TBOOL, YP_VNONE }, diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h index 616ba2381d..0ddd7be4ef 100644 --- a/src/knot/conf/schema.h +++ b/src/knot/conf/schema.h @@ -122,7 +122,6 @@ #define C_USER "\x04""user" #define C_VERSION "\x07""version" #define C_VIA "\x03""via" -#define C_XDP_WORKERS "\x0B""xdp-workers" #define C_ZONE "\x04""zone" #define C_ZONEFILE_LOAD "\x0D""zonefile-load" #define C_ZONEFILE_SYNC "\x0D""zonefile-sync" diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c index 276987c94e..833ccc9959 100644 --- a/src/knot/conf/tools.c +++ b/src/knot/conf/tools.c @@ -312,13 +312,6 @@ int check_server( CHECK_LEGACY_NAME(C_SRV, C_MAX_IPV4_UDP_PAYLOAD, C_UDP_MAX_PAYLOAD_IPV4); CHECK_LEGACY_NAME(C_SRV, C_MAX_IPV6_UDP_PAYLOAD, C_UDP_MAX_PAYLOAD_IPV6); -#ifndef ENABLE_XDP - if (conf_xdp_threads_txn(args->extra->conf, args->extra->txn) > 0) { - args->err_str = "not compiled with XDP support"; - return KNOT_EINVAL; - } -#endif - return KNOT_EOK; } diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c index a20b4b2f47..0ffab8c4c7 100644 --- a/src/knot/ctl/commands.c +++ b/src/knot/ctl/commands.c @@ -1385,10 +1385,8 @@ static int server_status(ctl_args_t *args) int running_bkg_wrk, wrk_queue; worker_pool_status(args->server->workers, &running_bkg_wrk, &wrk_queue); ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers %zu, " - "XDP workers: %zu, " "background workers: %zu (running: %d, pending: %d)", conf()->cache.srv_udp_threads, conf()->cache.srv_tcp_threads, - conf()->cache.srv_xdp_threads, conf()->cache.srv_bg_threads, running_bkg_wrk, wrk_queue); } else if (strcasecmp(type, "configure") == 0) { ret = snprintf(buff, sizeof(buff), "%s", CONFIGURE_SUMMARY); diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c index b6c3c369ff..6768e498a0 100644 --- a/src/knot/nameserver/query_module.c +++ b/src/knot/nameserver/query_module.c @@ -345,9 +345,6 @@ knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env) case KNOTD_CONF_ENV_WORKERS_UDP: out.single.integer = config->cache.srv_udp_threads; break; - case KNOTD_CONF_ENV_WORKERS_XDP: - out.single.integer = config->cache.srv_xdp_threads; - break; case KNOTD_CONF_ENV_WORKERS_TCP: out.single.integer = config->cache.srv_tcp_threads; break; diff --git a/src/knot/server/server.c b/src/knot/server/server.c index f52ef04f87..00752562bd 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -240,6 +240,113 @@ static int iface_addr2name(const struct sockaddr_storage *add, char **out) return matches == 0 ? KNOT_EADDRNOTAVAIL : KNOT_ELIMIT; } +// returns >= 0: the number of RX queues; < 0 : error +static int get_xdp_iface(struct sockaddr_storage *addr, char **out_devname, uint16_t *out_port) +{ +#ifndef ENABLE_XDP + return 0; +#else + + char *dev = NULL; + int port = 0; + if (addr->ss_family == AF_UNIX) { + dev = strdup(((struct sockaddr_un *)addr)->sun_path); + if (dev == NULL) { + log_warning("%s", knot_strerror(KNOT_ENOMEM)); + return KNOT_ENOMEM; + } + char *sport = strchr(dev, '@'); + if (sport == NULL || (port = atoi(sport + 1)) < 1 || port > 0xffff) { + log_warning("Wrong format of XDP listen, expected 'dev@port': '%s'", dev); + free(dev); + return KNOT_EINVAL; + } + *sport = '\0'; + } else { + int ret = iface_addr2name(addr, &dev); + if (ret != KNOT_EOK) { + if (dev != NULL) { + log_warning("XDP failed: address %s corresponds to %s net devices", dev, ret == KNOT_EADDRNOTAVAIL ? "none" : "more"); + } else { + log_warning("failed to find dev for address (%s)", knot_strerror(ret)); + } + return ret; + } + port = sockaddr_port(addr); + if (port < 1 || port > 0xffff) { + log_warning("port out of range"); + free(dev); + return KNOT_ELIMIT; + } + } + + int rx_queues = knot_eth_get_rx_queues(dev); + if (rx_queues < 0) { + log_warning("failed to obtain RX queue count for iface %s (%s)\n", dev, knot_strerror(rx_queues)); + free(dev); + return rx_queues; + } + + if (out_devname != NULL) { + *out_devname = dev; + } else { + free(dev); + } + if (out_port != NULL) { + *out_port = port; + } + return rx_queues; +#endif +} + +static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, unsigned *thread_id_start) +{ +#ifndef ENABLE_XDP + assert(0); + return NULL; +#else + char *dev = NULL; + uint16_t port = 0; + int rx_queues = get_xdp_iface(addr, &dev, &port); + if (rx_queues < 0) { + return NULL; + } + + iface_t *new_if = calloc(1, sizeof(*new_if)); + if (new_if == NULL) { + log_error("failed to initialize interface"); + free(dev); + return NULL; + } + memcpy(&new_if->addr, addr, sizeof(*addr)); + + new_if->fd_xdp = malloc(rx_queues * sizeof(int)); + new_if->sock_xdp = calloc(rx_queues, sizeof(*new_if->sock_xdp)); + if (new_if->fd_xdp == NULL || new_if->sock_xdp == NULL) { + log_warning("failed to init XDP: not enough memory\n"); + free(new_if); + free(dev); + return NULL; + } + new_if->fd_thread_ids = *thread_id_start; + *thread_id_start += rx_queues; + + for (int i = 0; i < rx_queues; i++) { + int ret = knot_xsk_init(new_if->sock_xdp + i, dev, i, port, i == 0); + + if (ret != KNOT_EOK) { + log_warning("failed to init XDP in dev %s queue %d (%s)", dev, i, knot_strerror(ret)); + new_if->fd_xdp[i] = -1; + } else { + new_if->fd_xdp[i] = knot_xsk_get_poll_fd(new_if->sock_xdp[i]); + + } + new_if->fd_xdp_count++; + } + return new_if; +#endif +} + /*! * \brief Create and initialize new interface. * @@ -255,7 +362,7 @@ static int iface_addr2name(const struct sockaddr_storage *add, char **out) */ static iface_t *server_init_iface(struct sockaddr_storage *addr, int udp_thread_count, int tcp_thread_count, - int xdp_thread_count, bool tcp_reuseport) + bool tcp_reuseport) { iface_t *new_if = calloc(1, sizeof(*new_if)); if (new_if == NULL) { @@ -272,7 +379,6 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, int udp_bind_flags = 0; int tcp_socket_count = 1; int tcp_bind_flags = 0; - int xdp_socket_count = 0; #ifdef ENABLE_REUSEPORT udp_socket_count = udp_thread_count; @@ -284,10 +390,6 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, } #endif -#ifdef ENABLE_XDP - xdp_socket_count = xdp_thread_count; -#endif - new_if->fd_udp = malloc(udp_socket_count * sizeof(int)); new_if->fd_tcp = malloc(tcp_socket_count * sizeof(int)); @@ -344,56 +446,6 @@ static iface_t *server_init_iface(struct sockaddr_storage *addr, new_if->fd_udp_count += 1; } - if (xdp_socket_count > 0) { -#ifndef ENABLE_XDP - assert(0); -#else - char *dev = NULL; - int ret = iface_addr2name(addr, &dev); - if (ret != KNOT_EOK) { - if (dev != NULL) { - log_warning("XDP failed: address %s corresponds to %s net devices", dev, ret == KNOT_EADDRNOTAVAIL ? "none" : "more"); - } else { - log_warning("failed to find dev for address (%s)", knot_strerror(ret)); - } - goto skip_xdp; - } - - int rx_queues = knot_eth_get_rx_queues(dev); - if (rx_queues < 0) { - log_warning("failed to obtain RX queue count for iface %s (%s)\n", dev, knot_strerror(rx_queues)); - goto skip_xdp; - } - - assert(new_if->fd_xdp == NULL); - assert(new_if->sock_xdp == NULL); - new_if->fd_xdp = malloc(rx_queues * sizeof(int)); - new_if->sock_xdp = calloc(rx_queues, sizeof(*new_if->sock_xdp)); - if (new_if->fd_xdp == NULL || new_if->sock_xdp == NULL) { - log_warning("failed to init XDP: not enough memory\n"); - goto skip_xdp; - } - - for (int i = 0; i < rx_queues; i++) { - assert(sockaddr_port(addr) < (1 << 16)); - ret = knot_xsk_init(new_if->sock_xdp + i, dev, i, - sockaddr_port(addr), i == 0); - - if (ret != KNOT_EOK) { - log_warning("failed to init XDP in dev %s queue %d (%s)", dev, i, knot_strerror(ret)); - new_if->fd_xdp[i] = -1; - } else { - new_if->fd_xdp[i] = knot_xsk_get_poll_fd(new_if->sock_xdp[i]); - - } - new_if->fd_xdp_count++; - } -skip_xdp: - free(dev); -#endif - } - - warn_bind = true; warn_bufsize = true; warn_flag_misc = true; @@ -458,7 +510,12 @@ static int configure_sockets(conf_t *conf, server_t *s) log_info("using reuseport for UDP%s", conf->cache.srv_tcp_reuseport ? " and TCP" : ""); #endif - if (conf->cache.srv_xdp_threads > 0) { + /* Update bound interfaces. */ + conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN); + conf_val_t lisxdp_val = conf_get(conf, C_SRV, C_LISTEN_XDP); + conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR); + + if (lisxdp_val.code == KNOT_EOK) { struct rlimit no_limit = { RLIM_INFINITY, RLIM_INFINITY }; int ret = setrlimit(RLIMIT_MEMLOCK, &no_limit); if (ret) { @@ -466,11 +523,7 @@ static int configure_sockets(conf_t *conf, server_t *s) } } - /* Update bound interfaces. */ - conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN); - conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR); - - size_t nifs = conf_val_count(&listen_val), real_n = 0; + size_t nifs = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val), real_n = 0; iface_t *newlist = calloc(nifs, sizeof(*newlist)); if (newlist == NULL) { return KNOT_ENOMEM; @@ -488,7 +541,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; - iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, conf->cache.srv_xdp_threads, tcp_reuseport); + iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp, tcp_reuseport); if (new_if != NULL) { memcpy(&newlist[real_n++], new_if, sizeof(*newlist)); free(new_if); @@ -496,6 +549,20 @@ static int configure_sockets(conf_t *conf, server_t *s) conf_val_next(&listen_val); } + unsigned thread_id = s->handlers[IO_UDP].handler.unit->size + s->handlers[IO_TCP].handler.unit->size; + while (lisxdp_val.code == KNOT_EOK) { + struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL); + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), &addr); + log_info("binding to xdp interface %s", addr_str); + + iface_t *new_if = server_init_xdp_iface(&addr, &thread_id); + if (new_if != NULL) { + memcpy(&newlist[real_n++], new_if, sizeof(*newlist)); + free(new_if); + } + conf_val_next(&lisxdp_val); + } assert(real_n <= nifs); nifs = real_n; free(rundir); @@ -516,6 +583,22 @@ static int configure_sockets(conf_t *conf, server_t *s) return KNOT_EOK; } +int server_count_xdp_threads(conf_t *conf) +{ + int res = 0, ret; + conf_val_t lisxdp_val = conf_get(conf, C_SRV, C_LISTEN_XDP); + while (lisxdp_val.code == KNOT_EOK) { + struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL); + ret = get_xdp_iface(&addr, NULL, NULL); + if (ret < 0) { + return ret; + } + res += ret; + conf_val_next(&lisxdp_val); + } + return res; +} + int server_init(server_t *server, int bg_workers) { if (server == NULL) { @@ -782,7 +865,6 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) static bool warn_tcp_reuseport = true; static bool warn_udp = true; - static bool warn_xdp = true; static bool warn_tcp = true; static bool warn_bg = true; static bool warn_listen = true; @@ -797,11 +879,6 @@ static void warn_server_reconfigure(conf_t *conf, server_t *server) warn_udp = false; } - if (warn_xdp && server->handlers[IO_XDP].size != conf_xdp_threads(conf)) { - log_warning(msg, "xdp-workers"); - warn_xdp = false; - } - if (warn_tcp && server->handlers[IO_TCP].size != conf_tcp_threads(conf)) { log_warning(msg, "tcp-workers"); warn_tcp = false; @@ -907,7 +984,7 @@ void server_stop(server_t *server) server->state &= ~ServerRunning; } -static int set_handler(server_t *server, int index, unsigned size, bool use_xdp, runnable_t run) +static int set_handler(server_t *server, int index, unsigned size, runnable_t run) { /* Initialize I/O handlers. */ int ret = server_init_handler(server, index, size, run, NULL); @@ -916,27 +993,26 @@ static int set_handler(server_t *server, int index, unsigned size, bool use_xdp, } server->handlers[index].size = size; - server->handlers[index].handler.use_xdp = use_xdp; return KNOT_EOK; } /*! \brief Reconfigure UDP and TCP query processing threads. */ -static int configure_threads(conf_t *conf, server_t *server) +static int configure_threads(conf_t *conf, server_t *server, size_t xdp_threads) { - int ret = set_handler(server, IO_UDP, conf->cache.srv_udp_threads, false, udp_master); + int ret = set_handler(server, IO_UDP, conf->cache.srv_udp_threads, udp_master); if (ret != KNOT_EOK) { return ret; } - if (conf->cache.srv_xdp_threads > 0) { - ret = set_handler(server, IO_XDP, conf->cache.srv_xdp_threads, true, udp_master); + if (xdp_threads > 0) { + ret = set_handler(server, IO_XDP, xdp_threads, udp_master); if (ret != KNOT_EOK) { return ret; } } - return set_handler(server, IO_TCP, conf->cache.srv_tcp_threads, false, tcp_master); + return set_handler(server, IO_TCP, conf->cache.srv_tcp_threads, tcp_master); } static int reconfigure_journal_db(conf_t *conf, server_t *server) @@ -996,8 +1072,14 @@ void server_reconfigure(conf_t *conf, server_t *server) knot_db_lmdb_get_path(conf->db)); } + if ((ret = server_count_xdp_threads(conf)) < 0) { + log_error("failed to configure XDP thread count (%s)", + knot_strerror(ret)); + ret = 0; + } + /* Configure server threads. */ - if ((ret = configure_threads(conf, server)) != KNOT_EOK) { + if ((ret = configure_threads(conf, server, ret)) != KNOT_EOK) { log_error("failed to configure server threads (%s)", knot_strerror(ret)); } diff --git a/src/knot/server/server.h b/src/knot/server/server.h index c834261c5c..697c079c46 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -39,7 +39,6 @@ typedef struct iohandler { dt_unit_t *unit; /*!< Threading unit */ unsigned *thread_state; /*!< Thread state */ unsigned *thread_id; /*!< Thread identifier. */ - bool use_xdp; /*!< Using XDP. */ } iohandler_t; /*! \brief Server state flags. @@ -59,6 +58,7 @@ typedef struct iface { int fd_tcp_count; int *fd_xdp; int fd_xdp_count; + unsigned fd_thread_ids; struct knot_xsk_socket **sock_xdp; struct sockaddr_storage addr; } iface_t; diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index ef8080b2d5..0402bbfaf4 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. +/* Copyright (C) 2020 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 @@ -133,6 +133,9 @@ static unsigned tcp_set_ifaces(const iface_t *ifaces, size_t n_ifaces, fdset_t * fdset_clear(fds); for (const iface_t *i = ifaces; i != ifaces + n_ifaces; i++) { int tcp_id = 0; + if (i->fd_tcp_count < 1) { + continue; + } #ifdef ENABLE_REUSEPORT if (conf()->cache.srv_tcp_reuseport) { /* Note: thread_ids start with UDP threads, TCP threads follow. */ diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index dcb93eed7a..46b31a955d 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -450,21 +450,36 @@ static udp_api_t xdp_recvmmsg_api = { }; #endif /* ENABLE_XDP */ +static bool is_xdp_iface(const iface_t *iface) +{ + bool is_xdp1 = (iface->fd_xdp_count > 0); + bool is_xdp2 = (iface->fd_udp_count == 0 && iface->fd_tcp_count == 0); + assert(is_xdp1 == is_xdp2); + return is_xdp1 || is_xdp2; +} + +static bool is_xdp_thread(const iface_t *iface_zero, int thread_id) +{ + return (thread_id >= iface_zero->fd_udp_count + iface_zero->fd_tcp_count); +} + /*! \brief Get interface UDP descriptor for a given thread. */ -static int iface_udp_fd(const iface_t *iface, int thread_id, bool use_xdp, void **socket_ctx) +static int iface_udp_fd(const iface_t *iface, const iface_t *iface_zero, int thread_id, void **socket_ctx) { - if (use_xdp) { -#ifdef ENABLE_XDP - size_t udp_wrk = conf()->cache.srv_udp_threads; - size_t tcp_wrk = conf()->cache.srv_tcp_threads; - size_t xdp_wrk = conf()->cache.srv_xdp_threads; - // XDP worker thread follow after UDP and TCP worker threads - assert(thread_id >= udp_wrk + tcp_wrk); - assert(thread_id < udp_wrk + tcp_wrk + xdp_wrk); + assert(!is_xdp_iface(iface_zero)); + + bool xdp_iface = is_xdp_iface(iface); + bool xdp_thread = is_xdp_thread(iface_zero, thread_id); + assert(xdp_thread || thread_id < iface_zero->fd_udp_count); - size_t xdp_wrk_id = thread_id - udp_wrk - tcp_wrk; + if (xdp_iface != xdp_thread) { + return -1; + } - if (xdp_wrk_id >= iface->fd_xdp_count) { + if (xdp_thread) { +#ifdef ENABLE_XDP + size_t xdp_wrk_id = thread_id - iface->fd_thread_ids; + if (thread_id < iface->fd_thread_ids || xdp_wrk_id >= iface->fd_xdp_count) { return -1; } @@ -494,7 +509,7 @@ static int iface_udp_fd(const iface_t *iface, int thread_id, bool use_xdp, void * \return Number of watched descriptors, zero on error. */ static unsigned udp_set_ifaces(const iface_t *ifaces, size_t n_ifaces, struct pollfd **fds_ptr, - int thread_id, bool use_xdp, void **socket_ctxs) + int thread_id, void **socket_ctxs) { memset(socket_ctxs, 0, n_ifaces * sizeof(*socket_ctxs)); @@ -508,9 +523,9 @@ static unsigned udp_set_ifaces(const iface_t *ifaces, size_t n_ifaces, struct po } for (size_t i = 0; i < n_ifaces; i++) { - fds[i].fd = iface_udp_fd(&ifaces[i], thread_id, use_xdp, &socket_ctxs[i]); + fds[i].fd = iface_udp_fd(&ifaces[i], &ifaces[0], thread_id, &socket_ctxs[i]); if (fds[i].fd < 0) { - return 0; + continue; } fds[i].events = POLLIN; fds[i].revents = 0; @@ -536,8 +551,12 @@ int udp_master(dthread_t *thread) /* Prepare structures for bound sockets. */ unsigned thr_id = dt_get_id(thread); iohandler_t *handler = (iohandler_t *)thread->data; + if (handler->server->n_ifaces == 0) { + return KNOT_EOK; + } + udp_api_t *api = NULL; - if (handler->use_xdp) { + if (is_xdp_thread(&handler->server->ifaces[0], handler->thread_id[thr_id])) { #ifndef ENABLE_XDP assert(0); #else @@ -570,7 +589,7 @@ int udp_master(dthread_t *thread) size_t nifs = handler->server->n_ifaces; void *socket_ctxs[nifs]; // only for XDP: pointers on knot_xsk_socket unsigned nfds = udp_set_ifaces(handler->server->ifaces, handler->server->n_ifaces, &fds, - udp.thread_id, handler->use_xdp, socket_ctxs); + udp.thread_id, socket_ctxs); if (nfds == 0) { goto finish; } diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c index aec2ae12f5..9eb2b7ee86 100644 --- a/tests/knot/test_confio.c +++ b/tests/knot/test_confio.c @@ -909,7 +909,6 @@ static void test_conf_io_list(void) "server.tcp-max-clients\n" "server.tcp-reuseport\n" "server.udp-workers\n" - "server.xdp-workers\n" "server.tcp-workers\n" "server.background-workers\n" "server.udp-max-payload\n" @@ -932,7 +931,6 @@ static const yp_item_t desc_server[] = { { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE }, { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE }, { C_UDP_WORKERS, YP_TINT, YP_VNONE }, - { C_XDP_WORKERS, YP_TINT, YP_VNONE }, { C_TCP_WORKERS, YP_TINT, YP_VNONE }, { C_BG_WORKERS, YP_TINT, YP_VNONE }, { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VNONE },