From: Ondřej Surý Date: Mon, 16 Sep 2024 07:10:36 +0000 (+0200) Subject: Limit the outgoing UDP send queue size X-Git-Tag: v9.21.2~34^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b576c4c9771e967e035e3fe761cd256a2067f754;p=thirdparty%2Fbind9.git 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. --- diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 1b7e881b5b6..d45734ecc77 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 653ef778d34..82f99a61c8d 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -640,6 +641,19 @@ 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_now(); + isc_stdtime_t last = atomic_exchange_relaxed(&last_udpsends_log, now); + if (now != last) { + return (true); + } + + return (false); +} + /* * Send the data in 'region' to a peer via a UDP socket. We try to find * a proper sibling/child socket so that we won't have to jump to @@ -697,12 +711,39 @@ isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, goto fail; } - r = uv_udp_send(&uvreq->uv_req.udp_send, &sock->uv_handle.udp, - &uvreq->uvbuf, 1, sa, udp_send_cb); - if (r < 0) { - isc__nm_incstats(sock, STATID_SENDFAIL); - result = isc_uverr2result(r); - goto fail; + 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, &uvreq->uvbuf, 1, sa); + if (r < 0) { + if (can_log_udp_sends()) { + isc__netmgr_log( + worker->netmgr, ISC_LOG_ERROR, + "Sending UDP messages failed: %s", + isc_result_totext(isc_uverr2result(r))); + } + + isc__nm_incstats(sock, STATID_SENDFAIL); + result = isc_uverr2result(r); + goto fail; + } + + RUNTIME_CHECK(r == (int)region->length); + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, true); + + } else { + /* Send the message asynchronously */ + r = uv_udp_send(&uvreq->uv_req.udp_send, &sock->uv_handle.udp, + &uvreq->uvbuf, 1, sa, udp_send_cb); + if (r < 0) { + isc__nm_incstats(sock, STATID_SENDFAIL); + result = isc_uverr2result(r); + goto fail; + } } return; fail: