Path: blob/main/dns/bind916/files/bind-v9.16.0-tcp_quota_fix.patch
16125 views
diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h1index ae83f943d3..c85065f39d 1006442--- a/lib/isc/netmgr/netmgr-int.h3+++ b/lib/isc/netmgr/netmgr-int.h4@@ -356,7 +356,16 @@ struct isc_nmsocket {5*/6isc_quota_t *quota;7isc_quota_t *pquota;8- bool overquota;9+10+ /*%11+ * How many connections we have not accepted due to quota?12+ * When we close a connection we need to accept a new one.13+ */14+ int overquota;15+ /*%16+ * How many active connections we have?17+ */18+ int conns;1920/*%21* Socket statistics22diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c23index f4361575cc..26728c1ba6 10064424--- a/lib/isc/netmgr/netmgr.c25+++ b/lib/isc/netmgr/netmgr.c26@@ -727,6 +727,11 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree)27for (int i = 0; i < sock->nchildren; i++) {28if (!atomic_load(&sock->children[i].destroying)) {29nmsocket_cleanup(&sock->children[i], false);30+ if (sock->statsindex != NULL) {31+ isc__nm_decstats(32+ sock->mgr,33+ sock->statsindex[STATID_ACTIVE]);34+ }35}36}3738@@ -738,6 +743,9 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree)39sock->children = NULL;40sock->nchildren = 0;41}42+ if (sock->statsindex != NULL) {43+ isc__nm_decstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);44+ }4546if (sock->tcphandle != NULL) {47isc_nmhandle_unref(sock->tcphandle);48@@ -854,8 +862,6 @@ isc__nmsocket_prep_destroy(isc_nmsocket_t *sock)49if (sock->children != NULL) {50for (int i = 0; i < sock->nchildren; i++) {51atomic_store(&sock->children[i].active, false);52- isc__nm_decstats(sock->mgr,53- sock->statsindex[STATID_ACTIVE]);54}55}5657diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c58index a83fede0d2..58ffd3c404 10064459--- a/lib/isc/netmgr/tcp.c60+++ b/lib/isc/netmgr/tcp.c61@@ -26,12 +26,28 @@62#include <isc/region.h>63#include <isc/result.h>64#include <isc/sockaddr.h>65+#include <isc/stdtime.h>66#include <isc/thread.h>67#include <isc/util.h>6869#include "netmgr-int.h"70#include "uv-compat.h"7172+static atomic_uint_fast32_t last_tcpquota_log = ATOMIC_VAR_INIT(0);73+74+static bool75+can_log_tcp_quota() {76+ isc_stdtime_t now, last;77+78+ isc_stdtime_get(&now);79+ last = atomic_exchange_relaxed(&last_tcpquota_log, now);80+ if (now != last) {81+ return (true);82+ }83+84+ return (false);85+}86+87static int88tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);8990@@ -668,9 +684,6 @@ read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)91}9293isc__nm_free_uvbuf(sock, buf);94- if (sock->quota) {95- isc_quota_detach(&sock->quota);96- }9798/*99* This might happen if the inner socket is closing. It means that100@@ -699,6 +712,7 @@ accept_connection(isc_nmsocket_t *ssock)101struct sockaddr_storage ss;102isc_sockaddr_t local;103int r;104+ bool overquota = false;105106REQUIRE(VALID_NMSOCK(ssock));107REQUIRE(ssock->tid == isc_nm_tid());108@@ -711,10 +725,25 @@ accept_connection(isc_nmsocket_t *ssock)109110if (ssock->pquota != NULL) {111result = isc_quota_attach(ssock->pquota, "a);112+113+ /*114+ * We share the quota between all TCP sockets. Others115+ * may have used up all the quota slots, in which case116+ * this socket could starve. So we only fail here if we117+ * already had at least one active connection on this118+ * socket. This guarantees that we'll maintain some level119+ * of service while over quota, and will resume normal120+ * service when the quota comes back down.121+ */122if (result != ISC_R_SUCCESS) {123- isc__nm_incstats(ssock->mgr,124- ssock->statsindex[STATID_ACCEPTFAIL]);125- return (result);126+ ssock->overquota++;127+ overquota = true;128+ if (ssock->conns > 0) {129+ isc__nm_incstats(130+ ssock->mgr,131+ ssock->statsindex[STATID_ACCEPTFAIL]);132+ return (result);133+ }134}135}136137@@ -761,6 +790,7 @@ accept_connection(isc_nmsocket_t *ssock)138}139140isc_nmsocket_attach(ssock, &csock->server);141+ ssock->conns++;142143handle = isc__nmhandle_get(csock, NULL, &local);144145@@ -779,6 +809,9 @@ error:146if (csock->quota != NULL) {147isc_quota_detach(&csock->quota);148}149+ if (overquota) {150+ ssock->overquota--;151+ }152/* We need to detach it properly to make sure uv_close is called. */153isc_nmsocket_detach(&csock);154return (result);155@@ -793,14 +826,14 @@ tcp_connection_cb(uv_stream_t *server, int status)156UNUSED(status);157158result = accept_connection(ssock);159- if (result != ISC_R_SUCCESS) {160- if (result == ISC_R_QUOTA || result == ISC_R_SOFTQUOTA) {161- ssock->overquota = true;162+ if (result != ISC_R_SUCCESS && result != ISC_R_NOCONN) {163+ if ((result != ISC_R_QUOTA && result != ISC_R_SOFTQUOTA) ||164+ can_log_tcp_quota()) {165+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,166+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,167+ "TCP connection failed: %s",168+ isc_result_totext(result));169}170- isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,171- ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,172- "TCP connection failed: %s",173- isc_result_totext(result));174}175}176177@@ -936,17 +969,27 @@ tcp_close_direct(isc_nmsocket_t *sock)178REQUIRE(VALID_NMSOCK(sock));179REQUIRE(sock->tid == isc_nm_tid());180REQUIRE(sock->type == isc_nm_tcpsocket);181+ isc_nmsocket_t *ssock = sock->server;182183if (sock->quota != NULL) {184- isc_nmsocket_t *ssock = sock->server;185-186isc_quota_detach(&sock->quota);187-188- if (ssock->overquota) {189+ }190+ if (ssock != NULL) {191+ ssock->conns--;192+ while (ssock->conns == 0 && ssock->overquota > 0) {193+ ssock->overquota--;194isc_result_t result = accept_connection(ssock);195- if (result != ISC_R_QUOTA &&196- result != ISC_R_SOFTQUOTA) {197- ssock->overquota = false;198+ if (result == ISC_R_SUCCESS || result == ISC_R_NOCONN) {199+ continue;200+ }201+ if ((result != ISC_R_QUOTA &&202+ result != ISC_R_SOFTQUOTA) ||203+ can_log_tcp_quota()) {204+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,205+ ISC_LOGMODULE_NETMGR,206+ ISC_LOG_ERROR,207+ "TCP connection failed: %s",208+ isc_result_totext(result));209}210}211}212diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c213index e384b73be9..f89eb359af 100644214--- a/lib/isc/netmgr/tcpdns.c215+++ b/lib/isc/netmgr/tcpdns.c216@@ -43,6 +43,9 @@ dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg);217static void218resume_processing(void *arg);219220+static void221+tcpdns_close_direct(isc_nmsocket_t *sock);222+223static inline size_t224dnslen(unsigned char *base)225{226@@ -82,7 +85,6 @@ timer_close_cb(uv_handle_t *handle)227{228isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);229INSIST(VALID_NMSOCK(sock));230- atomic_store(&sock->closed, true);231isc_nmsocket_detach(&sock);232}233234@@ -94,9 +96,7 @@ dnstcp_readtimeout(uv_timer_t *timer)235236REQUIRE(VALID_NMSOCK(sock));237REQUIRE(sock->tid == isc_nm_tid());238-239- isc_nmsocket_detach(&sock->outer);240- uv_close((uv_handle_t *)&sock->timer, timer_close_cb);241+ tcpdns_close_direct(sock);242}243244/*245@@ -252,7 +252,9 @@ dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg)246* We have a packet: stop timeout timers247*/248atomic_store(&dnssock->outer->processing, true);249- uv_timer_stop(&dnssock->timer);250+ if (dnssock->timer_initialized) {251+ uv_timer_stop(&dnssock->timer);252+ }253254if (atomic_load(&dnssock->sequential)) {255/*256@@ -399,8 +401,10 @@ resume_processing(void *arg)257if (atomic_load(&sock->ah) == 0) {258/* Nothing is active; sockets can timeout now */259atomic_store(&sock->outer->processing, false);260- uv_timer_start(&sock->timer, dnstcp_readtimeout,261- sock->read_timeout, 0);262+ if (sock->timer_initialized) {263+ uv_timer_start(&sock->timer, dnstcp_readtimeout,264+ sock->read_timeout, 0);265+ }266}267268/*269@@ -413,7 +417,9 @@ resume_processing(void *arg)270result = processbuffer(sock, &handle);271if (result == ISC_R_SUCCESS) {272atomic_store(&sock->outer->processing, true);273- uv_timer_stop(&sock->timer);274+ if (sock->timer_initialized) {275+ uv_timer_stop(&sock->timer);276+ }277isc_nmhandle_unref(handle);278} else if (sock->outer != NULL) {279isc_nm_resumeread(sock->outer);280@@ -441,7 +447,9 @@ resume_processing(void *arg)281break;282}283284- uv_timer_stop(&sock->timer);285+ if (sock->timer_initialized) {286+ uv_timer_stop(&sock->timer);287+ }288atomic_store(&sock->outer->processing, true);289isc_nmhandle_unref(dnshandle);290} while (atomic_load(&sock->ah) < TCPDNS_CLIENTS_PER_CONN);291@@ -507,18 +515,29 @@ static void292tcpdns_close_direct(isc_nmsocket_t *sock)293{294REQUIRE(sock->tid == isc_nm_tid());295- if (sock->outer != NULL) {296- sock->outer->rcb.recv = NULL;297- isc_nmsocket_detach(&sock->outer);298- }299- if (sock->listener != NULL) {300- isc_nmsocket_detach(&sock->listener);301- }302/* We don't need atomics here, it's all in single network thread */303if (sock->timer_initialized) {304+ /*305+ * We need to fire the timer callback to clean it up,306+ * it will then call us again (via detach) so that we307+ * can finally close the socket.308+ */309sock->timer_initialized = false;310uv_timer_stop(&sock->timer);311uv_close((uv_handle_t *)&sock->timer, timer_close_cb);312+ } else {313+ /*314+ * At this point we're certain that there are no external315+ * references, we can close everything.316+ */317+ if (sock->outer != NULL) {318+ sock->outer->rcb.recv = NULL;319+ isc_nmsocket_detach(&sock->outer);320+ }321+ if (sock->listener != NULL) {322+ isc_nmsocket_detach(&sock->listener);323+ }324+ atomic_store(&sock->closed, true);325}326}327328diff --git a/lib/isc/netmgr/uverr2result.c b/lib/isc/netmgr/uverr2result.c329index b6a8065e3e..9781454ca6 100644330--- a/lib/isc/netmgr/uverr2result.c331+++ b/lib/isc/netmgr/uverr2result.c332@@ -38,6 +38,8 @@ isc___nm_uverr2result(int uverr, bool dolog, const char *file,333return (ISC_R_INVALIDFILE);334case UV_ENOENT:335return (ISC_R_FILENOTFOUND);336+ case UV_EAGAIN:337+ return (ISC_R_NOCONN);338case UV_EACCES:339case UV_EPERM:340return (ISC_R_NOPERM);341342343