From 3012a97d58fd04841b1bcff0c9850806d2e30e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Mon, 16 Sep 2024 09:10:36 +0200 Subject: [PATCH] Limit the outgoing UDP send queue size If the operating system UDP queue gets full and the outgoing UDP sending starts to be delayed, BIND 9 could exhibit memory spikes as it tries to enqueue all the outgoing UDP messages. As those are not going to be delivered anyway (as we argued when we stopped enlarging the operating system send and receive buffers), try to send the UDP messages directly using `uv_udp_try_send()` and if that fails, drop the outgoing UDP message. (cherry picked from commit b576c4c9771e967e035e3fe761cd256a2067f754) --- lib/isc/netmgr/netmgr-int.h | 1 + lib/isc/netmgr/udp.c | 50 ++++++++++++++++++++++++++++++++++--- lib/isc/netmgr/uv-compat.h | 1 + 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index c13b6fae99..f4cb5dd8fc 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -60,6 +60,7 @@ */ #define ISC_NETMGR_UDP_RECVBUF_SIZE UINT16_MAX #endif +#define ISC_NETMGR_UDP_SENDBUF_SIZE UINT16_MAX /* * The TCP send and receive buffers can fit one maximum sized DNS message plus diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 1956a185ef..f9f528ffb1 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -809,6 +811,21 @@ udp_send_cb(uv_udp_send_t *req, int status) { isc__nm_sendcb(sock, uvreq, result, false); } +static _Atomic(isc_stdtime_t) last_udpsends_log = 0; + +static bool +can_log_udp_sends(void) { + isc_stdtime_t now; + + isc_stdtime_get(&now); + isc_stdtime_t last = atomic_exchange_relaxed(&last_udpsends_log, now); + if (now != last) { + return (true); + } + + return (false); +} + /* * udp_send_direct sends buf to a peer on a socket. Sock has to be in * the same thread as the callee. @@ -840,10 +857,35 @@ udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, } #endif - r = uv_udp_send(&req->uv_req.udp_send, &sock->uv_handle.udp, - &req->uvbuf, 1, sa, udp_send_cb); - if (r < 0) { - return (isc__nm_uverr2result(r)); + if (uv_udp_get_send_queue_size(&sock->uv_handle.udp) > + ISC_NETMGR_UDP_SENDBUF_SIZE) + { + /* + * The kernel UDP send queue is full, try sending the UDP + * response synchronously instead of just failing. + */ + r = uv_udp_try_send(&sock->uv_handle.udp, &req->uvbuf, 1, sa); + if (r < 0) { + if (can_log_udp_sends()) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_NETMGR, + ISC_LOG_ERROR, + "Sending UDP messages failed: %s", + isc_result_totext( + isc__nm_uverr2result(r))); + } + + return (isc__nm_uverr2result(r)); + } + + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); + } else { + /* Send the message asynchronously */ + r = uv_udp_send(&req->uv_req.udp_send, &sock->uv_handle.udp, + &req->uvbuf, 1, sa, udp_send_cb); + if (r < 0) { + return (isc__nm_uverr2result(r)); + } } return (ISC_R_SUCCESS); diff --git a/lib/isc/netmgr/uv-compat.h b/lib/isc/netmgr/uv-compat.h index f0a0d2de00..ec99208830 100644 --- a/lib/isc/netmgr/uv-compat.h +++ b/lib/isc/netmgr/uv-compat.h @@ -127,4 +127,5 @@ isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr, #if UV_VERSION_HEX < UV_VERSION(1, 19, 0) #define uv_stream_get_write_queue_size(stream) ((stream)->write_queue_size) +#define uv_udp_get_send_queue_size(handle) ((handle)->send_queue_size) #endif /* UV_VERSION_HEX < UV_VERSION(1, 19, 0) */