]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Limit the outgoing UDP send queue size
authorOndřej Surý <ondrej@isc.org>
Mon, 16 Sep 2024 07:10:36 +0000 (09:10 +0200)
committerOndřej Surý <ondrej@isc.org>
Tue, 17 Sep 2024 14:02:03 +0000 (14:02 +0000)
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.

lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/udp.c

index 1b7e881b5b61a0b2e131c6888b3864824ea8c278..d45734ecc77b2299ae4f6d82eaaf808056417226 100644 (file)
@@ -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
index 653ef778d349224c0c7ec9fab548372cb8080a94..82f99a61c8d4443343fcf2022fdc6ae467c8f2fa 100644 (file)
@@ -27,6 +27,7 @@
 #include <isc/region.h>
 #include <isc/result.h>
 #include <isc/sockaddr.h>
+#include <isc/stdtime.h>
 #include <isc/thread.h>
 #include <isc/util.h>
 #include <isc/uv.h>
@@ -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: