mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-03 16:15:27 +00:00
Refactor the isc_quota code and fix the quota in TCP accept code
In e185412872
, the TCP accept quota code
became broken in a subtle way - the quota would get initialized on the
first accept for the server socket and then deleted from the server
socket, so it would never get applied again.
Properly fixing this required a bigger refactoring of the isc_quota API
code to make it much simpler. The new code decouples the ownership of
the quota and acquiring/releasing the quota limit.
After (during) the refactoring it became more clear that we need to use
the callback from the child side of the accepted connection, and not the
server side.
This commit is contained in:
@@ -31,26 +31,20 @@
|
||||
***/
|
||||
|
||||
#include <isc/atomic.h>
|
||||
#include <isc/job.h>
|
||||
#include <isc/lang.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
/*****
|
||||
***** Types.
|
||||
*****/
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
#undef ISC_QUOTA_TRACE
|
||||
|
||||
/*% isc_quota_cb - quota callback structure */
|
||||
typedef struct isc_quota_cb isc_quota_cb_t;
|
||||
typedef void (*isc_quota_cb_func_t)(isc_quota_t *quota, void *data);
|
||||
struct isc_quota_cb {
|
||||
int magic;
|
||||
isc_quota_cb_func_t cb_func;
|
||||
void *data;
|
||||
ISC_LINK(isc_quota_cb_t) link;
|
||||
};
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
/*% isc_quota structure */
|
||||
struct isc_quota {
|
||||
@@ -60,7 +54,7 @@ struct isc_quota {
|
||||
atomic_uint_fast32_t soft;
|
||||
atomic_uint_fast32_t waiting;
|
||||
isc_mutex_t cblock;
|
||||
ISC_LIST(isc_quota_cb_t) cbs;
|
||||
ISC_LIST(isc_job_t) jobs;
|
||||
ISC_LINK(isc_quota_t) link;
|
||||
};
|
||||
|
||||
@@ -106,25 +100,14 @@ isc_quota_getused(isc_quota_t *quota);
|
||||
* Get the current usage of quota.
|
||||
*/
|
||||
|
||||
#define isc_quota_acquire(quota) isc_quota_acquire_cb(quota, NULL, NULL, NULL)
|
||||
isc_result_t
|
||||
isc_quota_attach(isc_quota_t *quota, isc_quota_t **p);
|
||||
isc_quota_acquire_cb(isc_quota_t *quota, isc_job_t *job, isc_job_cb cb,
|
||||
void *cbarg);
|
||||
/*%<
|
||||
*
|
||||
* Attempt to reserve one unit of 'quota', and also attaches '*p' to the quota
|
||||
* if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA).
|
||||
*
|
||||
* Returns:
|
||||
* \li #ISC_R_SUCCESS Success
|
||||
* \li #ISC_R_SOFTQUOTA Success soft quota reached
|
||||
* \li #ISC_R_QUOTA Quota is full
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **p, isc_quota_cb_t *cb);
|
||||
/*%<
|
||||
*
|
||||
* Like isc_quota_attach(), but if there's no quota left then cb->cb_func will
|
||||
* be called when we are attached to quota.
|
||||
* Attempt to reserve one unit of 'quota', if there's no quota left then
|
||||
* cb->cb(cb->cbarg) will be called when there's quota again.
|
||||
*
|
||||
* Note: It's the caller's responsibility to make sure that we don't end up
|
||||
* with a huge number of callbacks waiting, making it easy to create a
|
||||
@@ -140,15 +123,9 @@ isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **p, isc_quota_cb_t *cb);
|
||||
*/
|
||||
|
||||
void
|
||||
isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data);
|
||||
isc_quota_release(isc_quota_t *quota);
|
||||
/*%<
|
||||
* Initialize isc_quota_cb_t - setup the list, set the callback and data.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_quota_detach(isc_quota_t **p);
|
||||
/*%<
|
||||
* Release one unit of quota, and also detaches '*p' from the quota.
|
||||
* Release one unit of quota.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
@@ -527,14 +527,11 @@ struct isc_nmsocket {
|
||||
const char *tls_verify_error;
|
||||
} streamdns;
|
||||
/*%
|
||||
* quota is the TCP client, attached when a TCP connection
|
||||
* is established. pquota is a non-attached pointer to the
|
||||
* TCP client quota, stored in listening sockets but only
|
||||
* attached in connected sockets.
|
||||
* pquota is a non-attached pointer to the TCP client quota, stored in
|
||||
* listening sockets.
|
||||
*/
|
||||
isc_quota_t *quota;
|
||||
isc_quota_t *pquota;
|
||||
isc_quota_cb_t quotacb;
|
||||
isc_job_t quotacb;
|
||||
|
||||
/*%
|
||||
* Socket statistics
|
||||
@@ -1282,8 +1279,6 @@ void
|
||||
isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
||||
isc_result_t eresult, bool async);
|
||||
void
|
||||
isc__nm_failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult);
|
||||
void
|
||||
isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
||||
isc_result_t eresult, bool async);
|
||||
void
|
||||
|
@@ -487,10 +487,7 @@ nmsocket_cleanup(void *arg) {
|
||||
nmhandle_free(sock, handle);
|
||||
}
|
||||
|
||||
if (sock->quota != NULL) {
|
||||
isc_quota_detach(&sock->quota);
|
||||
}
|
||||
|
||||
INSIST(sock->server == NULL);
|
||||
sock->pquota = NULL;
|
||||
|
||||
isc__nm_tls_cleanup_data(sock);
|
||||
@@ -707,6 +704,7 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker,
|
||||
.active_link = ISC_LINK_INITIALIZER,
|
||||
.active = true,
|
||||
.job = ISC_JOB_INITIALIZER,
|
||||
.quotacb = ISC_JOB_INITIALIZER,
|
||||
};
|
||||
|
||||
if (iface != NULL) {
|
||||
@@ -1058,35 +1056,6 @@ isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) {
|
||||
REQUIRE(sock->accepting);
|
||||
REQUIRE(sock->server);
|
||||
|
||||
/*
|
||||
* Detach the quota early to make room for other connections;
|
||||
* otherwise it'd be detached later asynchronously, and clog
|
||||
* the quota unnecessarily.
|
||||
*/
|
||||
if (sock->quota != NULL) {
|
||||
isc_quota_detach(&sock->quota);
|
||||
}
|
||||
|
||||
isc__nmsocket_detach(&sock->server);
|
||||
|
||||
sock->accepting = false;
|
||||
|
||||
switch (eresult) {
|
||||
case ISC_R_NOTCONNECTED:
|
||||
/* IGNORE: The client disconnected before we could accept */
|
||||
break;
|
||||
default:
|
||||
isc__nmsocket_log(sock, ISC_LOG_ERROR,
|
||||
"Accepting TCP connection failed: %s",
|
||||
isc_result_totext(eresult));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
||||
isc_result_t eresult, bool async) {
|
||||
|
@@ -72,39 +72,7 @@ static isc_result_t
|
||||
accept_connection(isc_nmsocket_t *ssock);
|
||||
|
||||
static void
|
||||
quota_accept_cb(isc_quota_t *quota, void *arg);
|
||||
|
||||
static void
|
||||
failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult);
|
||||
|
||||
static void
|
||||
failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) {
|
||||
REQUIRE(sock->server);
|
||||
REQUIRE(sock->accepting);
|
||||
|
||||
sock->accepting = false;
|
||||
|
||||
/*
|
||||
* Detach the quota early to make room for other connections;
|
||||
* otherwise it'd be detached later asynchronously, and clog
|
||||
* the quota unnecessarily.
|
||||
*/
|
||||
if (sock->quota != NULL) {
|
||||
isc_quota_detach(&sock->quota);
|
||||
}
|
||||
|
||||
isc__nmsocket_detach(&sock->server);
|
||||
|
||||
switch (eresult) {
|
||||
case ISC_R_NOTCONNECTED:
|
||||
/* IGNORE: The client disconnected before we could accept */
|
||||
break;
|
||||
default:
|
||||
isc__nmsocket_log(sock, ISC_LOG_ERROR,
|
||||
"Accepting TCP connection failed: %s",
|
||||
isc_result_totext(eresult));
|
||||
}
|
||||
}
|
||||
quota_accept_cb(void *arg);
|
||||
|
||||
static isc_result_t
|
||||
tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
|
||||
@@ -458,8 +426,7 @@ start_tcp_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock,
|
||||
csock->backlog = sock->backlog;
|
||||
|
||||
/*
|
||||
* We don't attach to quota, just assign - to avoid
|
||||
* increasing quota unnecessarily.
|
||||
* Quota isn't attached, just assigned.
|
||||
*/
|
||||
csock->pquota = sock->pquota;
|
||||
|
||||
@@ -562,6 +529,8 @@ tcp_connection_cb(uv_stream_t *server, int status) {
|
||||
isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server);
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(ssock->accept_cb != NULL);
|
||||
|
||||
if (status != 0) {
|
||||
result = isc_uverr2result(status);
|
||||
goto done;
|
||||
@@ -575,18 +544,24 @@ tcp_connection_cb(uv_stream_t *server, int status) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ssock->pquota != NULL) {
|
||||
isc_quota_t *quota = NULL;
|
||||
isc_quota_cb_init(&ssock->quotacb, quota_accept_cb, ssock);
|
||||
result = isc_quota_attach_cb(ssock->pquota, "a,
|
||||
&ssock->quotacb);
|
||||
/* Prepare the child socket */
|
||||
isc_nmsocket_t *csock = isc_mem_get(ssock->worker->mctx,
|
||||
sizeof(isc_nmsocket_t));
|
||||
isc__nmsocket_init(csock, ssock->worker, isc_nm_tcpsocket,
|
||||
&ssock->iface, NULL);
|
||||
isc__nmsocket_attach(ssock, &csock->server);
|
||||
|
||||
if (csock->server->pquota != NULL) {
|
||||
result = isc_quota_acquire_cb(csock->server->pquota,
|
||||
&csock->quotacb, quota_accept_cb,
|
||||
csock);
|
||||
if (result == ISC_R_QUOTA) {
|
||||
isc__nm_incstats(ssock, STATID_ACCEPTFAIL);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
result = accept_connection(ssock);
|
||||
result = accept_connection(csock);
|
||||
done:
|
||||
isc__nm_accept_connection_log(ssock, result, can_log_tcp_quota());
|
||||
}
|
||||
@@ -700,14 +675,6 @@ isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
|
||||
|
||||
destroy:
|
||||
isc__nmsocket_prep_destroy(sock);
|
||||
|
||||
/*
|
||||
* We need to detach from quota after the read callback function had a
|
||||
* chance to be executed.
|
||||
*/
|
||||
if (sock->quota != NULL) {
|
||||
isc_quota_detach(&sock->quota);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -849,76 +816,67 @@ tcpaccept_cb(void *arg) {
|
||||
|
||||
isc_result_t result = accept_connection(sock);
|
||||
isc__nm_accept_connection_log(sock, result, can_log_tcp_quota());
|
||||
sock->pquota = NULL;
|
||||
isc__nmsocket_detach(&sock);
|
||||
}
|
||||
|
||||
static void
|
||||
quota_accept_cb(isc_quota_t *quota, void *arg) {
|
||||
isc_nmsocket_t *sock = arg;
|
||||
quota_accept_cb(void *arg) {
|
||||
isc_nmsocket_t *csock = arg;
|
||||
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
|
||||
UNUSED(quota);
|
||||
REQUIRE(VALID_NMSOCK(csock));
|
||||
|
||||
/*
|
||||
* This needs to be asynchronous, because the quota might have been
|
||||
* released by a different child socket.
|
||||
*/
|
||||
if (sock->tid == isc_tid()) {
|
||||
isc_result_t result = accept_connection(sock);
|
||||
isc__nm_accept_connection_log(sock, result,
|
||||
if (csock->tid == isc_tid()) {
|
||||
isc_result_t result = accept_connection(csock);
|
||||
isc__nm_accept_connection_log(csock, result,
|
||||
can_log_tcp_quota());
|
||||
sock->pquota = NULL;
|
||||
} else {
|
||||
isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
|
||||
isc_async_run(sock->worker->loop, tcpaccept_cb, sock);
|
||||
isc__nmsocket_attach(csock, &(isc_nmsocket_t *){ NULL });
|
||||
isc_async_run(csock->worker->loop, tcpaccept_cb, csock);
|
||||
}
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
accept_connection(isc_nmsocket_t *ssock) {
|
||||
isc_nmsocket_t *csock = NULL;
|
||||
isc__networker_t *worker = NULL;
|
||||
accept_connection(isc_nmsocket_t *csock) {
|
||||
int r;
|
||||
isc_result_t result;
|
||||
struct sockaddr_storage ss;
|
||||
isc_sockaddr_t local;
|
||||
isc_nmhandle_t *handle = NULL;
|
||||
|
||||
REQUIRE(VALID_NMSOCK(ssock));
|
||||
REQUIRE(ssock->tid == isc_tid());
|
||||
REQUIRE(VALID_NMSOCK(csock));
|
||||
REQUIRE(VALID_NMSOCK(csock->server));
|
||||
REQUIRE(csock->tid == isc_tid());
|
||||
|
||||
if (isc__nmsocket_closing(ssock)) {
|
||||
if (ssock->pquota != NULL) {
|
||||
isc_quota_detach(&ssock->pquota);
|
||||
}
|
||||
|
||||
return (ISC_R_CANCELED);
|
||||
}
|
||||
|
||||
REQUIRE(ssock->accept_cb != NULL);
|
||||
|
||||
csock = isc_mem_get(ssock->worker->mctx, sizeof(isc_nmsocket_t));
|
||||
isc__nmsocket_init(csock, ssock->worker, isc_nm_tcpsocket,
|
||||
&ssock->iface, NULL);
|
||||
isc__nmsocket_attach(ssock, &csock->server);
|
||||
csock->recv_cb = ssock->recv_cb;
|
||||
csock->recv_cbarg = ssock->recv_cbarg;
|
||||
csock->accepting = true;
|
||||
csock->quota = ssock->pquota;
|
||||
csock->accept_cb = csock->server->accept_cb;
|
||||
csock->accept_cbarg = csock->server->accept_cbarg;
|
||||
csock->recv_cb = csock->server->recv_cb;
|
||||
csock->recv_cbarg = csock->server->recv_cbarg;
|
||||
csock->read_timeout = atomic_load_relaxed(&csock->worker->netmgr->init);
|
||||
|
||||
worker = csock->worker;
|
||||
|
||||
r = uv_tcp_init(&worker->loop->loop, &csock->uv_handle.tcp);
|
||||
r = uv_tcp_init(&csock->worker->loop->loop, &csock->uv_handle.tcp);
|
||||
UV_RUNTIME_CHECK(uv_tcp_init, r);
|
||||
uv_handle_set_data(&csock->uv_handle.handle, csock);
|
||||
|
||||
r = uv_timer_init(&worker->loop->loop, &csock->read_timer);
|
||||
r = uv_timer_init(&csock->worker->loop->loop, &csock->read_timer);
|
||||
UV_RUNTIME_CHECK(uv_timer_init, r);
|
||||
uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock);
|
||||
|
||||
r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream);
|
||||
/*
|
||||
* We need to initialize the tcp and timer before failing because
|
||||
* isc__nm_tcp_close() can't handle uninitalized TCP nmsocket.
|
||||
*/
|
||||
if (isc__nmsocket_closing(csock)) {
|
||||
result = ISC_R_CANCELED;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
r = uv_accept(&csock->server->uv_handle.stream,
|
||||
&csock->uv_handle.stream);
|
||||
if (r != 0) {
|
||||
result = isc_uverr2result(r);
|
||||
goto failure;
|
||||
@@ -951,7 +909,7 @@ accept_connection(isc_nmsocket_t *ssock) {
|
||||
|
||||
handle = isc__nmhandle_get(csock, NULL, &local);
|
||||
|
||||
result = ssock->accept_cb(handle, ISC_R_SUCCESS, ssock->accept_cbarg);
|
||||
result = csock->accept_cb(handle, ISC_R_SUCCESS, csock->accept_cbarg);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc_nmhandle_detach(&handle);
|
||||
goto failure;
|
||||
@@ -961,8 +919,6 @@ accept_connection(isc_nmsocket_t *ssock) {
|
||||
|
||||
isc__nm_incstats(csock, STATID_ACCEPT);
|
||||
|
||||
csock->read_timeout = atomic_load_relaxed(&csock->worker->netmgr->init);
|
||||
|
||||
/*
|
||||
* The acceptcb needs to attach to the handle if it wants to keep the
|
||||
* connection alive
|
||||
@@ -978,8 +934,14 @@ accept_connection(isc_nmsocket_t *ssock) {
|
||||
|
||||
failure:
|
||||
csock->active = false;
|
||||
csock->accepting = false;
|
||||
|
||||
failed_accept_cb(csock, result);
|
||||
if (result != ISC_R_NOTCONNECTED) {
|
||||
/* IGNORE: The client disconnected before we could accept */
|
||||
isc__nmsocket_log(csock, ISC_LOG_ERROR,
|
||||
"Accepting TCP connection failed: %s",
|
||||
isc_result_totext(result));
|
||||
}
|
||||
|
||||
isc__nmsocket_prep_destroy(csock);
|
||||
|
||||
@@ -1153,6 +1115,9 @@ tcp_close_sock(isc_nmsocket_t *sock) {
|
||||
isc__nm_incstats(sock, STATID_CLOSE);
|
||||
|
||||
if (sock->server != NULL) {
|
||||
if (sock->server->pquota != NULL) {
|
||||
isc_quota_release(sock->server->pquota);
|
||||
}
|
||||
isc__nmsocket_detach(&sock->server);
|
||||
}
|
||||
|
||||
@@ -1178,10 +1143,6 @@ isc__nm_tcp_close(isc_nmsocket_t *sock) {
|
||||
|
||||
sock->closing = true;
|
||||
|
||||
if (sock->quota != NULL) {
|
||||
isc_quota_detach(&sock->quota);
|
||||
}
|
||||
|
||||
/*
|
||||
* The order of the close operation is important here, the uv_close()
|
||||
* gets scheduled in the reverse order, so we need to close the timer
|
||||
|
@@ -944,11 +944,6 @@ udp_close_cb(uv_handle_t *handle) {
|
||||
|
||||
isc__nm_incstats(sock, STATID_CLOSE);
|
||||
|
||||
if (sock->server != NULL) {
|
||||
/* server socket (accept) */
|
||||
isc__nmsocket_detach(&sock->server);
|
||||
}
|
||||
|
||||
if (sock->parent != NULL) {
|
||||
/* listening socket (listen) */
|
||||
isc__nmsocket_detach(&sock);
|
||||
|
140
lib/isc/quota.c
140
lib/isc/quota.c
@@ -22,45 +22,28 @@
|
||||
#define QUOTA_MAGIC ISC_MAGIC('Q', 'U', 'O', 'T')
|
||||
#define VALID_QUOTA(p) ISC_MAGIC_VALID(p, QUOTA_MAGIC)
|
||||
|
||||
#define QUOTA_CB_MAGIC ISC_MAGIC('Q', 'T', 'C', 'B')
|
||||
#define VALID_QUOTA_CB(p) ISC_MAGIC_VALID(p, QUOTA_CB_MAGIC)
|
||||
|
||||
void
|
||||
isc_quota_init(isc_quota_t *quota, unsigned int max) {
|
||||
atomic_init("a->max, max);
|
||||
atomic_init("a->used, 0);
|
||||
atomic_init("a->soft, 0);
|
||||
atomic_init("a->waiting, 0);
|
||||
ISC_LIST_INIT(quota->cbs);
|
||||
ISC_LIST_INIT(quota->jobs);
|
||||
isc_mutex_init("a->cblock);
|
||||
ISC_LINK_INIT(quota, link);
|
||||
quota->magic = QUOTA_MAGIC;
|
||||
}
|
||||
|
||||
void
|
||||
isc_quota_destroy(isc_quota_t *quota) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
quota->magic = 0;
|
||||
|
||||
INSIST(atomic_load("a->used) == 0);
|
||||
INSIST(atomic_load("a->waiting) == 0);
|
||||
INSIST(ISC_LIST_EMPTY(quota->cbs));
|
||||
atomic_store_release("a->max, 0);
|
||||
atomic_store_release("a->used, 0);
|
||||
atomic_store_release("a->soft, 0);
|
||||
isc_mutex_destroy("a->cblock);
|
||||
}
|
||||
|
||||
void
|
||||
isc_quota_soft(isc_quota_t *quota, unsigned int soft) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
atomic_store_release("a->soft, soft);
|
||||
atomic_store_relaxed("a->soft, soft);
|
||||
}
|
||||
|
||||
void
|
||||
isc_quota_max(isc_quota_t *quota, unsigned int max) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
atomic_store_release("a->max, max);
|
||||
atomic_store_relaxed("a->max, max);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@@ -81,46 +64,27 @@ isc_quota_getused(isc_quota_t *quota) {
|
||||
return (atomic_load_relaxed("a->used));
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
quota_reserve(isc_quota_t *quota) {
|
||||
isc_result_t result;
|
||||
uint_fast32_t max = atomic_load_acquire("a->max);
|
||||
uint_fast32_t soft = atomic_load_acquire("a->soft);
|
||||
uint_fast32_t used = atomic_load_acquire("a->used);
|
||||
do {
|
||||
if (max != 0 && used >= max) {
|
||||
return (ISC_R_QUOTA);
|
||||
}
|
||||
if (soft != 0 && used >= soft) {
|
||||
result = ISC_R_SOFTQUOTA;
|
||||
} else {
|
||||
result = ISC_R_SUCCESS;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak_acq_rel("a->used, &used,
|
||||
used + 1));
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* Must be quota->cbslock locked */
|
||||
/* Must be quota->cblock locked */
|
||||
static void
|
||||
enqueue(isc_quota_t *quota, isc_quota_cb_t *cb) {
|
||||
enqueue(isc_quota_t *quota, isc_job_t *cb) {
|
||||
REQUIRE(cb != NULL);
|
||||
ISC_LIST_ENQUEUE(quota->cbs, cb, link);
|
||||
atomic_fetch_add_release("a->waiting, 1);
|
||||
ISC_LIST_ENQUEUE(quota->jobs, cb, link);
|
||||
atomic_fetch_add_relaxed("a->waiting, 1);
|
||||
}
|
||||
|
||||
/* Must be quota->cbslock locked */
|
||||
static isc_quota_cb_t *
|
||||
/* Must be quota->cblock locked */
|
||||
static isc_job_t *
|
||||
dequeue(isc_quota_t *quota) {
|
||||
isc_quota_cb_t *cb = ISC_LIST_HEAD(quota->cbs);
|
||||
INSIST(cb != NULL);
|
||||
ISC_LIST_DEQUEUE(quota->cbs, cb, link);
|
||||
isc_job_t *cb = ISC_LIST_HEAD(quota->jobs);
|
||||
if (cb != NULL) {
|
||||
ISC_LIST_DEQUEUE(quota->jobs, cb, link);
|
||||
atomic_fetch_sub_relaxed("a->waiting, 1);
|
||||
}
|
||||
return (cb);
|
||||
}
|
||||
|
||||
static void
|
||||
quota_release(isc_quota_t *quota) {
|
||||
void
|
||||
isc_quota_release(isc_quota_t *quota) {
|
||||
uint_fast32_t used;
|
||||
|
||||
/*
|
||||
@@ -131,14 +95,12 @@ quota_release(isc_quota_t *quota) {
|
||||
*/
|
||||
|
||||
if (atomic_load_acquire("a->waiting) > 0) {
|
||||
isc_quota_cb_t *cb = NULL;
|
||||
isc_job_t *cb = NULL;
|
||||
LOCK("a->cblock);
|
||||
if (atomic_load_relaxed("a->waiting) > 0) {
|
||||
cb = dequeue(quota);
|
||||
}
|
||||
UNLOCK("a->cblock);
|
||||
if (cb != NULL) {
|
||||
cb->cb_func(quota, cb->data);
|
||||
cb->cb(cb->cbarg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -147,56 +109,42 @@ quota_release(isc_quota_t *quota) {
|
||||
INSIST(used > 0);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
doattach(isc_quota_t *quota, isc_quota_t **p) {
|
||||
isc_result_t result;
|
||||
REQUIRE(p != NULL && *p == NULL);
|
||||
|
||||
result = quota_reserve(quota);
|
||||
if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
|
||||
*p = quota;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_quota_attach(isc_quota_t *quota, isc_quota_t **quotap) {
|
||||
isc_quota_acquire_cb(isc_quota_t *quota, isc_job_t *job, isc_job_cb cb,
|
||||
void *cbarg) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
REQUIRE(quotap != NULL && *quotap == NULL);
|
||||
REQUIRE(job == NULL || cb != NULL);
|
||||
|
||||
return (isc_quota_attach_cb(quota, quotap, NULL));
|
||||
}
|
||||
uint_fast32_t used = atomic_fetch_add_release("a->used, 1);
|
||||
|
||||
isc_result_t
|
||||
isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **quotap,
|
||||
isc_quota_cb_t *cb) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
REQUIRE(cb == NULL || VALID_QUOTA_CB(cb));
|
||||
REQUIRE(quotap != NULL && *quotap == NULL);
|
||||
|
||||
isc_result_t result = doattach(quota, quotap);
|
||||
if (result == ISC_R_QUOTA && cb != NULL) {
|
||||
uint_fast32_t max = atomic_load_relaxed("a->max);
|
||||
if (max != 0 && used >= max) {
|
||||
(void)atomic_fetch_sub_relaxed("a->used, 1);
|
||||
if (job != NULL) {
|
||||
job->cb = cb;
|
||||
job->cbarg = cbarg;
|
||||
LOCK("a->cblock);
|
||||
enqueue(quota, cb);
|
||||
enqueue(quota, job);
|
||||
UNLOCK("a->cblock);
|
||||
}
|
||||
return (result);
|
||||
return (ISC_R_QUOTA);
|
||||
}
|
||||
|
||||
uint_fast32_t soft = atomic_load_relaxed("a->soft);
|
||||
if (soft != 0 && used >= soft) {
|
||||
return (ISC_R_SOFTQUOTA);
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data) {
|
||||
ISC_LINK_INIT(cb, link);
|
||||
cb->cb_func = cb_func;
|
||||
cb->data = data;
|
||||
cb->magic = QUOTA_CB_MAGIC;
|
||||
}
|
||||
isc_quota_destroy(isc_quota_t *quota) {
|
||||
REQUIRE(VALID_QUOTA(quota));
|
||||
quota->magic = 0;
|
||||
|
||||
void
|
||||
isc_quota_detach(isc_quota_t **quotap) {
|
||||
REQUIRE(quotap != NULL && VALID_QUOTA(*quotap));
|
||||
isc_quota_t *quota = *quotap;
|
||||
*quotap = NULL;
|
||||
|
||||
quota_release(quota);
|
||||
INSIST(atomic_load("a->used) == 0);
|
||||
INSIST(atomic_load("a->waiting) == 0);
|
||||
INSIST(ISC_LIST_EMPTY(quota->jobs));
|
||||
isc_mutex_destroy("a->cblock);
|
||||
}
|
||||
|
@@ -254,13 +254,6 @@ ns_client_endrequest(ns_client_t *client) {
|
||||
dns_ecs_init(&client->ecs);
|
||||
dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
|
||||
|
||||
/*
|
||||
* Ensure there are no recursions that still need to be cleaned up.
|
||||
*/
|
||||
for (int i = 0; i < RECTYPE_COUNT; i++) {
|
||||
INSIST(client->query.recursions[i].quota == NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all client attributes that are specific to the request
|
||||
*/
|
||||
@@ -1760,10 +1753,6 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
||||
client->attributes |= NS_CLIENTATTR_TCP;
|
||||
}
|
||||
|
||||
for (int i = 0; i < RECTYPE_COUNT; i++) {
|
||||
INSIST(client->query.recursions[i].quota == NULL);
|
||||
}
|
||||
|
||||
INSIST(client->state == NS_CLIENTSTATE_READY);
|
||||
|
||||
(void)atomic_fetch_add_relaxed(&ns_client_requests, 1);
|
||||
|
@@ -80,21 +80,6 @@ typedef enum {
|
||||
#define FETCH_RECTYPE_HOOK(client) \
|
||||
((client)->query.recursions[RECTYPE_HOOK].fetch)
|
||||
|
||||
/*%
|
||||
* Helper macros for accessing isc_quota_t pointers for a specific recursion a
|
||||
* given client is associated with.
|
||||
*/
|
||||
#define QUOTA_RECTYPE_NORMAL(client) \
|
||||
((client)->query.recursions[RECTYPE_NORMAL].quota)
|
||||
#define QUOTA_RECTYPE_PREFETCH(client) \
|
||||
((client)->query.recursions[RECTYPE_PREFETCH].quota)
|
||||
#define QUOTA_RECTYPE_RPZ(client) \
|
||||
((client)->query.recursions[RECTYPE_RPZ].quota)
|
||||
#define QUOTA_RECTYPE_STALE_REFRESH(client) \
|
||||
((client)->query.recursions[RECTYPE_STALE_REFRESH].quota)
|
||||
#define QUOTA_RECTYPE_HOOK(client) \
|
||||
((client)->query.recursions[RECTYPE_HOOK].quota)
|
||||
|
||||
/*%
|
||||
* nameserver recursion parameters, to uniquely identify a recursion
|
||||
* query; this is used to detect a recursion loop
|
||||
@@ -152,7 +137,6 @@ struct ns_query {
|
||||
struct {
|
||||
isc_nmhandle_t *handle;
|
||||
dns_fetch_t *fetch;
|
||||
isc_quota_t *quota;
|
||||
} recursions[RECTYPE_COUNT];
|
||||
|
||||
ns_query_recparam_t recparam;
|
||||
|
@@ -2483,13 +2483,10 @@ free_fresp(ns_client_t *client, dns_fetchresponse_t **frespp) {
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
recursionquotatype_attach(ns_client_t *client,
|
||||
ns_query_rectype_t recursion_type, bool soft_limit) {
|
||||
isc_quota_t **quotap = &client->query.recursions[recursion_type].quota;
|
||||
recursionquotatype_attach(ns_client_t *client, bool soft_limit) {
|
||||
isc_result_t result;
|
||||
|
||||
result = isc_quota_attach(&client->manager->sctx->recursionquota,
|
||||
quotap);
|
||||
result = isc_quota_acquire(&client->manager->sctx->recursionquota);
|
||||
switch (result) {
|
||||
case ISC_R_SUCCESS:
|
||||
break;
|
||||
@@ -2503,7 +2500,7 @@ recursionquotatype_attach(ns_client_t *client,
|
||||
break;
|
||||
}
|
||||
|
||||
isc_quota_detach(quotap);
|
||||
isc_quota_release(&client->manager->sctx->recursionquota);
|
||||
FALLTHROUGH;
|
||||
default:
|
||||
return (result);
|
||||
@@ -2516,21 +2513,18 @@ recursionquotatype_attach(ns_client_t *client,
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
recursionquotatype_attach_hard(ns_client_t *client,
|
||||
ns_query_rectype_t recursion_type) {
|
||||
return (recursionquotatype_attach(client, recursion_type, false));
|
||||
recursionquotatype_attach_hard(ns_client_t *client) {
|
||||
return (recursionquotatype_attach(client, false));
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
recursionquotatype_attach_soft(ns_client_t *client,
|
||||
ns_query_rectype_t recursion_type) {
|
||||
return (recursionquotatype_attach(client, recursion_type, true));
|
||||
recursionquotatype_attach_soft(ns_client_t *client) {
|
||||
return (recursionquotatype_attach(client, true));
|
||||
}
|
||||
|
||||
static void
|
||||
recursionquotatype_detach(ns_client_t *client,
|
||||
ns_query_rectype_t recursion_type) {
|
||||
isc_quota_detach(&client->query.recursions[recursion_type].quota);
|
||||
recursionquotatype_detach(ns_client_t *client) {
|
||||
isc_quota_release(&client->manager->sctx->recursionquota);
|
||||
ns_stats_decrement(client->manager->sctx->nsstats,
|
||||
ns_statscounter_recursclients);
|
||||
}
|
||||
@@ -2646,7 +2640,7 @@ cleanup_after_fetch(dns_fetchresponse_t *resp, const char *ctracestr,
|
||||
stale_refresh_aftermath(client, result);
|
||||
}
|
||||
|
||||
recursionquotatype_detach(client, recursion_type);
|
||||
recursionquotatype_detach(client);
|
||||
free_fresp(client, &resp);
|
||||
isc_nmhandle_detach(handlep);
|
||||
}
|
||||
@@ -2685,7 +2679,7 @@ fetch_and_forget(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t qtype,
|
||||
dns_fetch_t **fetchp;
|
||||
isc_result_t result;
|
||||
|
||||
result = recursionquotatype_attach_hard(client, recursion_type);
|
||||
result = recursionquotatype_attach_hard(client);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
@@ -2726,7 +2720,7 @@ fetch_and_forget(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t qtype,
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
ns_client_putrdataset(client, &tmprdataset);
|
||||
isc_nmhandle_detach(handlep);
|
||||
recursionquotatype_detach(client, recursion_type);
|
||||
recursionquotatype_detach(client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6299,7 +6293,7 @@ fetch_callback(void *arg) {
|
||||
* the manager's recursing-clients list.
|
||||
*/
|
||||
|
||||
recursionquotatype_detach(client, RECTYPE_NORMAL);
|
||||
recursionquotatype_detach(client);
|
||||
|
||||
LOCK(&client->manager->reclock);
|
||||
if (ISC_LINK_LINKED(client, rlink)) {
|
||||
@@ -6431,17 +6425,16 @@ static atomic_uint_fast32_t last_soft, last_hard;
|
||||
* Check recursion quota before making the current client "recursing".
|
||||
*/
|
||||
static isc_result_t
|
||||
check_recursionquota(ns_client_t *client, ns_query_rectype_t recursion_type) {
|
||||
isc_quota_t **quotap = &client->query.recursions[recursion_type].quota;
|
||||
check_recursionquota(ns_client_t *client) {
|
||||
isc_result_t result;
|
||||
|
||||
result = recursionquotatype_attach_soft(client, recursion_type);
|
||||
result = recursionquotatype_attach_soft(client);
|
||||
switch (result) {
|
||||
case ISC_R_SOFTQUOTA:
|
||||
recursionquota_log(client, &last_soft,
|
||||
"recursive-clients soft limit exceeded "
|
||||
"(%u/%u/%u), aborting oldest query",
|
||||
*quotap);
|
||||
&client->manager->sctx->recursionquota);
|
||||
ns_client_killoldestquery(client);
|
||||
FALLTHROUGH;
|
||||
case ISC_R_SUCCESS:
|
||||
@@ -6488,7 +6481,7 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
|
||||
inc_stats(client, ns_statscounter_recursion);
|
||||
}
|
||||
|
||||
result = check_recursionquota(client, RECTYPE_NORMAL);
|
||||
result = check_recursionquota(client);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
@@ -6534,7 +6527,7 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
|
||||
if (sigrdataset != NULL) {
|
||||
ns_client_putrdataset(client, &sigrdataset);
|
||||
}
|
||||
recursionquotatype_detach(client, RECTYPE_NORMAL);
|
||||
recursionquotatype_detach(client);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6764,7 +6757,7 @@ query_hookresume(void *arg) {
|
||||
UNLOCK(&client->query.fetchlock);
|
||||
SAVE(hctx, rev->ctx);
|
||||
|
||||
recursionquotatype_detach(client, RECTYPE_HOOK);
|
||||
recursionquotatype_detach(client);
|
||||
|
||||
LOCK(&client->manager->reclock);
|
||||
if (ISC_LINK_LINKED(client, rlink)) {
|
||||
@@ -6895,7 +6888,7 @@ ns_query_hookasync(query_ctx_t *qctx, ns_query_starthookasync_t runasync,
|
||||
REQUIRE(client->query.hookactx == NULL);
|
||||
REQUIRE(FETCH_RECTYPE_NORMAL(client) == NULL);
|
||||
|
||||
result = check_recursionquota(client, RECTYPE_HOOK);
|
||||
result = check_recursionquota(client);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -6924,7 +6917,7 @@ ns_query_hookasync(query_ctx_t *qctx, ns_query_starthookasync_t runasync,
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
cleanup_and_detach_from_quota:
|
||||
recursionquotatype_detach(client, RECTYPE_HOOK);
|
||||
recursionquotatype_detach(client);
|
||||
cleanup:
|
||||
/*
|
||||
* If we fail, send SERVFAIL now. It may be better to let the caller
|
||||
|
@@ -1882,8 +1882,7 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
|
||||
|
||||
update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK");
|
||||
|
||||
result = isc_quota_attach(&client->manager->sctx->updquota,
|
||||
&(isc_quota_t *){ NULL });
|
||||
result = isc_quota_acquire(&client->manager->sctx->updquota);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
||||
"update failed: too many DNS UPDATEs queued (%s)",
|
||||
@@ -3608,7 +3607,7 @@ updatedone_action(void *arg) {
|
||||
|
||||
respond(client, uev->result);
|
||||
|
||||
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
|
||||
isc_quota_release(&client->manager->sctx->updquota);
|
||||
if (uev->zone != NULL) {
|
||||
dns_zone_detach(&uev->zone);
|
||||
}
|
||||
@@ -3626,7 +3625,7 @@ forward_fail(void *arg) {
|
||||
|
||||
respond(client, DNS_R_SERVFAIL);
|
||||
|
||||
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
|
||||
isc_quota_release(&client->manager->sctx->updquota);
|
||||
isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
|
||||
isc_nmhandle_detach(&client->updatehandle);
|
||||
}
|
||||
@@ -3658,7 +3657,7 @@ forward_done(void *arg) {
|
||||
ns_client_sendraw(client, uev->answer);
|
||||
dns_message_detach(&uev->answer);
|
||||
|
||||
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
|
||||
isc_quota_release(&client->manager->sctx->updquota);
|
||||
isc_mem_put(client->manager->mctx, uev, sizeof(*uev));
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
isc_nmhandle_detach(&client->updatehandle);
|
||||
@@ -3696,9 +3695,11 @@ send_forward(ns_client_t *client, dns_zone_t *zone) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
result = isc_quota_attach(&client->manager->sctx->updquota,
|
||||
&(isc_quota_t *){ NULL });
|
||||
result = isc_quota_acquire(&client->manager->sctx->updquota);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (result == ISC_R_SOFTQUOTA) {
|
||||
isc_quota_release(&client->manager->sctx->updquota);
|
||||
}
|
||||
update_log(client, zone, LOGLEVEL_PROTOCOL,
|
||||
"update failed: too many DNS UPDATEs queued (%s)",
|
||||
isc_result_totext(result));
|
||||
|
@@ -649,7 +649,6 @@ typedef struct {
|
||||
dns_zone_t *zone; /* (necessary for stats) */
|
||||
dns_db_t *db;
|
||||
dns_dbversion_t *ver;
|
||||
isc_quota_t *quota;
|
||||
rrstream_t *stream; /* The XFR RR stream */
|
||||
bool question_added; /* QUESTION section sent? */
|
||||
bool end_of_stream; /* EOS has been reached */
|
||||
@@ -681,7 +680,7 @@ static void
|
||||
xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
|
||||
dns_name_t *qname, dns_rdatatype_t qtype,
|
||||
dns_rdataclass_t qclass, dns_zone_t *zone, dns_db_t *db,
|
||||
dns_dbversion_t *ver, isc_quota_t *quota, rrstream_t *stream,
|
||||
dns_dbversion_t *ver, rrstream_t *stream,
|
||||
dns_tsigkey_t *tsigkey, isc_buffer_t *lasttsig,
|
||||
bool verified_tsig, unsigned int maxtime,
|
||||
unsigned int idletime, bool many_answers,
|
||||
@@ -736,7 +735,6 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
|
||||
isc_mem_t *mctx = client->manager->mctx;
|
||||
dns_message_t *request = client->message;
|
||||
xfrout_ctx_t *xfr = NULL;
|
||||
isc_quota_t *quota = NULL;
|
||||
dns_transfer_format_t format = client->view->transfer_format;
|
||||
isc_netaddr_t na;
|
||||
dns_peer_t *peer = NULL;
|
||||
@@ -766,12 +764,12 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
|
||||
/*
|
||||
* Apply quota.
|
||||
*/
|
||||
result = isc_quota_attach(&client->manager->sctx->xfroutquota, "a);
|
||||
result = isc_quota_acquire(&client->manager->sctx->xfroutquota);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
|
||||
"%s request denied: %s", mnemonic,
|
||||
isc_result_totext(result));
|
||||
goto failure;
|
||||
goto max_quota;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1084,7 +1082,7 @@ have_stream:
|
||||
|
||||
if (is_dlz) {
|
||||
xfrout_ctx_create(mctx, client, request->id, question_name,
|
||||
reqtype, question_class, zone, db, ver, quota,
|
||||
reqtype, question_class, zone, db, ver,
|
||||
stream, dns_message_gettsigkey(request),
|
||||
tsigbuf, request->verified_sig, 3600, 3600,
|
||||
(format == dns_many_answers) ? true : false,
|
||||
@@ -1092,7 +1090,7 @@ have_stream:
|
||||
} else {
|
||||
xfrout_ctx_create(
|
||||
mctx, client, request->id, question_name, reqtype,
|
||||
question_class, zone, db, ver, quota, stream,
|
||||
question_class, zone, db, ver, stream,
|
||||
dns_message_gettsigkey(request), tsigbuf,
|
||||
request->verified_sig, dns_zone_getmaxxfrout(zone),
|
||||
dns_zone_getidleout(zone),
|
||||
@@ -1102,7 +1100,6 @@ have_stream:
|
||||
xfr->end_serial = current_serial;
|
||||
xfr->mnemonic = mnemonic;
|
||||
stream = NULL;
|
||||
quota = NULL;
|
||||
|
||||
CHECK(xfr->stream->methods->first(xfr->stream));
|
||||
|
||||
@@ -1172,9 +1169,6 @@ failure:
|
||||
if (result == DNS_R_REFUSED) {
|
||||
inc_stats(client, zone, ns_statscounter_xfrrej);
|
||||
}
|
||||
if (quota != NULL) {
|
||||
isc_quota_detach("a);
|
||||
}
|
||||
if (current_soa_tuple != NULL) {
|
||||
dns_difftuple_free(¤t_soa_tuple);
|
||||
}
|
||||
@@ -1196,10 +1190,12 @@ failure:
|
||||
if (zone != NULL) {
|
||||
dns_zone_detach(&zone);
|
||||
}
|
||||
/* XXX kludge */
|
||||
|
||||
if (xfr != NULL) {
|
||||
xfrout_fail(xfr, result, "setting up zone transfer");
|
||||
} else if (result != ISC_R_SUCCESS) {
|
||||
isc_quota_release(&client->manager->sctx->xfroutquota);
|
||||
max_quota:
|
||||
ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
|
||||
NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(3),
|
||||
"zone transfer setup failed");
|
||||
@@ -1212,7 +1208,7 @@ static void
|
||||
xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
|
||||
dns_name_t *qname, dns_rdatatype_t qtype,
|
||||
dns_rdataclass_t qclass, dns_zone_t *zone, dns_db_t *db,
|
||||
dns_dbversion_t *ver, isc_quota_t *quota, rrstream_t *stream,
|
||||
dns_dbversion_t *ver, rrstream_t *stream,
|
||||
dns_tsigkey_t *tsigkey, isc_buffer_t *lasttsig,
|
||||
bool verified_tsig, unsigned int maxtime,
|
||||
unsigned int idletime, bool many_answers,
|
||||
@@ -1279,7 +1275,6 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
|
||||
* These MUST be after the last "goto failure;" / CHECK to
|
||||
* prevent a double free by the caller.
|
||||
*/
|
||||
xfr->quota = quota;
|
||||
xfr->stream = stream;
|
||||
|
||||
*xfrp = xfr;
|
||||
@@ -1608,9 +1603,9 @@ xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
|
||||
if (xfr->lasttsig != NULL) {
|
||||
isc_buffer_free(&xfr->lasttsig);
|
||||
}
|
||||
if (xfr->quota != NULL) {
|
||||
isc_quota_detach(&xfr->quota);
|
||||
}
|
||||
|
||||
isc_quota_release(&xfr->client->manager->sctx->xfroutquota);
|
||||
|
||||
if (xfr->ver != NULL) {
|
||||
dns_db_closeversion(xfr->db, &xfr->ver, false);
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#define UNIT_TESTING
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <isc/job.h>
|
||||
#include <isc/quota.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/thread.h>
|
||||
@@ -31,10 +32,10 @@
|
||||
|
||||
#include <tests/isc.h>
|
||||
|
||||
isc_quota_t quota;
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_quota_get_set) {
|
||||
UNUSED(state);
|
||||
isc_quota_t quota;
|
||||
isc_quota_t *quota2 = NULL;
|
||||
isc_quota_init("a, 100);
|
||||
|
||||
assert_int_equal(isc_quota_getmax("a), 100);
|
||||
@@ -47,30 +48,26 @@ ISC_RUN_TEST_IMPL(isc_quota_get_set) {
|
||||
assert_int_equal(isc_quota_getsoft("a), 30);
|
||||
|
||||
assert_int_equal(isc_quota_getused("a), 0);
|
||||
isc_quota_attach("a, "a2);
|
||||
isc_quota_acquire("a);
|
||||
assert_int_equal(isc_quota_getused("a), 1);
|
||||
isc_quota_detach("a2);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(isc_quota_getused("a), 0);
|
||||
isc_quota_destroy("a);
|
||||
}
|
||||
|
||||
static void
|
||||
add_quota(isc_quota_t *source, isc_quota_t **target,
|
||||
isc_result_t expected_result, int expected_used) {
|
||||
add_quota(isc_quota_t *source, isc_result_t expected_result,
|
||||
int expected_used) {
|
||||
isc_result_t result;
|
||||
|
||||
*target = NULL;
|
||||
|
||||
result = isc_quota_attach(source, target);
|
||||
result = isc_quota_acquire(source);
|
||||
assert_int_equal(result, expected_result);
|
||||
|
||||
switch (expected_result) {
|
||||
case ISC_R_SUCCESS:
|
||||
case ISC_R_SOFTQUOTA:
|
||||
assert_ptr_equal(*target, source);
|
||||
break;
|
||||
default:
|
||||
assert_null(*target);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,30 +75,26 @@ add_quota(isc_quota_t *source, isc_quota_t **target,
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_quota_hard) {
|
||||
isc_quota_t quota;
|
||||
isc_quota_t *quotas[110];
|
||||
int i;
|
||||
UNUSED(state);
|
||||
|
||||
isc_quota_init("a, 100);
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
add_quota("a, "as[i], ISC_R_SUCCESS, i + 1);
|
||||
add_quota("a, ISC_R_SUCCESS, i + 1);
|
||||
}
|
||||
|
||||
add_quota("a, "as[100], ISC_R_QUOTA, 100);
|
||||
add_quota("a, ISC_R_QUOTA, 100);
|
||||
|
||||
assert_int_equal(isc_quota_getused("a), 100);
|
||||
|
||||
isc_quota_detach("as[0]);
|
||||
assert_null(quotas[0]);
|
||||
isc_quota_release("a);
|
||||
|
||||
add_quota("a, "as[100], ISC_R_SUCCESS, 100);
|
||||
add_quota("a, "as[101], ISC_R_QUOTA, 100);
|
||||
add_quota("a, ISC_R_SUCCESS, 100);
|
||||
add_quota("a, ISC_R_QUOTA, 100);
|
||||
|
||||
for (i = 100; i > 0; i--) {
|
||||
isc_quota_detach("as[i]);
|
||||
assert_null(quotas[i]);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(isc_quota_getused("a), i - 1);
|
||||
}
|
||||
assert_int_equal(isc_quota_getused("a), 0);
|
||||
@@ -109,8 +102,6 @@ ISC_RUN_TEST_IMPL(isc_quota_hard) {
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_quota_soft) {
|
||||
isc_quota_t quota;
|
||||
isc_quota_t *quotas[110];
|
||||
int i;
|
||||
UNUSED(state);
|
||||
|
||||
@@ -118,17 +109,16 @@ ISC_RUN_TEST_IMPL(isc_quota_soft) {
|
||||
isc_quota_soft("a, 50);
|
||||
|
||||
for (i = 0; i < 50; i++) {
|
||||
add_quota("a, "as[i], ISC_R_SUCCESS, i + 1);
|
||||
add_quota("a, ISC_R_SUCCESS, i + 1);
|
||||
}
|
||||
for (i = 50; i < 100; i++) {
|
||||
add_quota("a, "as[i], ISC_R_SOFTQUOTA, i + 1);
|
||||
add_quota("a, ISC_R_SOFTQUOTA, i + 1);
|
||||
}
|
||||
|
||||
add_quota("a, "as[i], ISC_R_QUOTA, 100);
|
||||
add_quota("a, ISC_R_QUOTA, 100);
|
||||
|
||||
for (i = 99; i >= 0; i--) {
|
||||
isc_quota_detach("as[i]);
|
||||
assert_null(quotas[i]);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(isc_quota_getused("a), i);
|
||||
}
|
||||
assert_int_equal(isc_quota_getused("a), 0);
|
||||
@@ -136,18 +126,14 @@ ISC_RUN_TEST_IMPL(isc_quota_soft) {
|
||||
}
|
||||
|
||||
static atomic_uint_fast32_t cb_calls = 0;
|
||||
static isc_quota_cb_t cbs[30];
|
||||
static isc_quota_t *qp;
|
||||
static isc_job_t cbs[30];
|
||||
|
||||
static void
|
||||
callback(isc_quota_t *quota, void *data) {
|
||||
callback(void *data) {
|
||||
int val = *(int *)data;
|
||||
/* Callback is not called if we get the quota directly */
|
||||
assert_int_not_equal(val, -1);
|
||||
|
||||
/* We get the proper quota pointer */
|
||||
assert_ptr_equal(quota, qp);
|
||||
|
||||
/* Verify that the callbacks are called in order */
|
||||
int v = atomic_fetch_add_relaxed(&cb_calls, 1);
|
||||
assert_int_equal(v, val);
|
||||
@@ -157,15 +143,12 @@ callback(isc_quota_t *quota, void *data) {
|
||||
* for the last 5 - do a 'chain detach'.
|
||||
*/
|
||||
if (v >= 5) {
|
||||
isc_quota_detach("a);
|
||||
isc_quota_release("a);
|
||||
}
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_quota_callback) {
|
||||
isc_result_t result;
|
||||
isc_quota_t quota;
|
||||
isc_quota_t *quotas[30];
|
||||
qp = "a;
|
||||
/*
|
||||
* - 10 calls that end with SUCCESS
|
||||
* - 10 calls that end with SOFTQUOTA
|
||||
@@ -181,51 +164,45 @@ ISC_RUN_TEST_IMPL(isc_quota_callback) {
|
||||
isc_quota_soft("a, 10);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
quotas[i] = NULL;
|
||||
isc_quota_cb_init(&cbs[i], callback, &ints[i]);
|
||||
result = isc_quota_attach_cb("a, "as[i], &cbs[i]);
|
||||
cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
|
||||
result = isc_quota_acquire_cb("a, &cbs[i], callback,
|
||||
&ints[i]);
|
||||
assert_int_equal(result, ISC_R_SUCCESS);
|
||||
assert_ptr_equal(quotas[i], "a);
|
||||
assert_int_equal(isc_quota_getused("a), i + 1);
|
||||
}
|
||||
for (i = 10; i < 20; i++) {
|
||||
quotas[i] = NULL;
|
||||
isc_quota_cb_init(&cbs[i], callback, &ints[i]);
|
||||
result = isc_quota_attach_cb("a, "as[i], &cbs[i]);
|
||||
cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
|
||||
result = isc_quota_acquire_cb("a, &cbs[i], callback,
|
||||
&ints[i]);
|
||||
assert_int_equal(result, ISC_R_SOFTQUOTA);
|
||||
assert_ptr_equal(quotas[i], "a);
|
||||
assert_int_equal(isc_quota_getused("a), i + 1);
|
||||
}
|
||||
|
||||
for (i = 20; i < 30; i++) {
|
||||
quotas[i] = NULL;
|
||||
isc_quota_cb_init(&cbs[i], callback, &ints[i]);
|
||||
result = isc_quota_attach_cb("a, "as[i], &cbs[i]);
|
||||
cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
|
||||
result = isc_quota_acquire_cb("a, &cbs[i], callback,
|
||||
&ints[i]);
|
||||
assert_int_equal(result, ISC_R_QUOTA);
|
||||
assert_ptr_equal(quotas[i], NULL);
|
||||
assert_int_equal(isc_quota_getused("a), 20);
|
||||
}
|
||||
assert_int_equal(atomic_load(&cb_calls), 0);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
isc_quota_detach("as[i]);
|
||||
assert_null(quotas[i]);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(isc_quota_getused("a), 20);
|
||||
assert_int_equal(atomic_load(&cb_calls), i + 1);
|
||||
}
|
||||
/* That should cause a chain reaction */
|
||||
isc_quota_detach("as[5]);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(atomic_load(&cb_calls), 10);
|
||||
|
||||
/* Release the quotas that we did not released in the callback */
|
||||
for (i = 0; i < 5; i++) {
|
||||
qp = "a;
|
||||
isc_quota_detach(&qp);
|
||||
isc_quota_release("a);
|
||||
}
|
||||
|
||||
for (i = 6; i < 20; i++) {
|
||||
isc_quota_detach("as[i]);
|
||||
assert_null(quotas[i]);
|
||||
isc_quota_release("a);
|
||||
assert_int_equal(isc_quota_getused("a), 19 - i);
|
||||
}
|
||||
assert_int_equal(atomic_load(&cb_calls), 10);
|
||||
@@ -244,8 +221,7 @@ ISC_RUN_TEST_IMPL(isc_quota_callback) {
|
||||
typedef struct qthreadinfo {
|
||||
atomic_uint_fast32_t direct;
|
||||
atomic_uint_fast32_t callback;
|
||||
isc_quota_t *quota;
|
||||
isc_quota_cb_t callbacks[100];
|
||||
isc_job_t callbacks[100];
|
||||
} qthreadinfo_t;
|
||||
|
||||
static atomic_uint_fast32_t g_tnum = 0;
|
||||
@@ -253,33 +229,31 @@ static atomic_uint_fast32_t g_tnum = 0;
|
||||
isc_thread_t g_threads[10 * 100];
|
||||
|
||||
static void *
|
||||
quota_detach(void *quotap) {
|
||||
isc_quota_t *quota = (isc_quota_t *)quotap;
|
||||
quota_release(void *arg) {
|
||||
uv_sleep(10);
|
||||
isc_quota_detach("a);
|
||||
isc_quota_release((isc_quota_t *)arg);
|
||||
return ((isc_threadresult_t)0);
|
||||
}
|
||||
|
||||
static void
|
||||
quota_callback(isc_quota_t *quota, void *data) {
|
||||
quota_callback(void *data) {
|
||||
qthreadinfo_t *qti = (qthreadinfo_t *)data;
|
||||
atomic_fetch_add_relaxed(&qti->callback, 1);
|
||||
int tnum = atomic_fetch_add_relaxed(&g_tnum, 1);
|
||||
isc_thread_create(quota_detach, quota, &g_threads[tnum]);
|
||||
isc_thread_create(quota_release, "a, &g_threads[tnum]);
|
||||
}
|
||||
|
||||
static isc_threadresult_t
|
||||
quota_thread(void *qtip) {
|
||||
qthreadinfo_t *qti = (qthreadinfo_t *)qtip;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
isc_quota_cb_init(&qti->callbacks[i], quota_callback, qti);
|
||||
isc_quota_t *quota = NULL;
|
||||
isc_result_t result = isc_quota_attach_cb(qti->quota, "a,
|
||||
&qti->callbacks[i]);
|
||||
qti->callbacks[i] = (isc_job_t)ISC_JOB_INITIALIZER;
|
||||
isc_result_t result = isc_quota_acquire_cb(
|
||||
"a, &qti->callbacks[i], quota_callback, qti);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
atomic_fetch_add_relaxed(&qti->direct, 1);
|
||||
int tnum = atomic_fetch_add_relaxed(&g_tnum, 1);
|
||||
isc_thread_create(quota_detach, quota,
|
||||
isc_thread_create(quota_release, "a,
|
||||
&g_threads[tnum]);
|
||||
}
|
||||
}
|
||||
@@ -288,7 +262,6 @@ quota_thread(void *qtip) {
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_quota_callback_mt) {
|
||||
UNUSED(state);
|
||||
isc_quota_t quota;
|
||||
int i;
|
||||
|
||||
isc_quota_init("a, 100);
|
||||
@@ -297,7 +270,6 @@ ISC_RUN_TEST_IMPL(isc_quota_callback_mt) {
|
||||
for (i = 0; i < 10; i++) {
|
||||
atomic_init(&qtis[i].direct, 0);
|
||||
atomic_init(&qtis[i].callback, 0);
|
||||
qtis[i].quota = "a;
|
||||
isc_thread_create(quota_thread, &qtis[i], &threads[i]);
|
||||
}
|
||||
for (i = 0; i < 10; i++) {
|
||||
|
@@ -861,7 +861,6 @@ run_hookasync_test(const ns__query_hookasync_test_params_t *test) {
|
||||
.action = ns_test_qctx_destroy_hook,
|
||||
.action_data = &asdata,
|
||||
};
|
||||
isc_quota_t *quota = NULL;
|
||||
isc_statscounter_t srvfail_cnt;
|
||||
bool expect_servfail = false;
|
||||
|
||||
@@ -899,7 +898,7 @@ run_hookasync_test(const ns__query_hookasync_test_params_t *test) {
|
||||
*/
|
||||
isc_quota_max(&sctx->recursionquota, 1);
|
||||
if (!test->quota_ok) {
|
||||
result = isc_quota_attach(&sctx->recursionquota, "a);
|
||||
result = isc_quota_acquire(&sctx->recursionquota);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -953,7 +952,6 @@ run_hookasync_test(const ns__query_hookasync_test_params_t *test) {
|
||||
/* Confirm necessary cleanup has been performed. */
|
||||
INSIST(qctx->client->query.hookactx == NULL);
|
||||
INSIST(qctx->client->state == NS_CLIENTSTATE_WORKING);
|
||||
INSIST(QUOTA_RECTYPE_HOOK(qctx->client) == NULL);
|
||||
INSIST(ns_stats_get_counter(
|
||||
qctx->client->manager->sctx->nsstats,
|
||||
ns_statscounter_recursclients) == 0);
|
||||
@@ -993,8 +991,8 @@ run_hookasync_test(const ns__query_hookasync_test_params_t *test) {
|
||||
*/
|
||||
ns_test_qctx_destroy(&qctx);
|
||||
ns_hooktable_free(mctx, (void **)&ns__hook_table);
|
||||
if (quota != NULL) {
|
||||
isc_quota_detach("a);
|
||||
if (!test->quota_ok) {
|
||||
isc_quota_release(&sctx->recursionquota);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user