***/
#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 {
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;
};
* 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
*/
void
-isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data);
-/*%<
- * Initialize isc_quota_cb_t - setup the list, set the callback and data.
- */
-
-void
-isc_quota_detach(isc_quota_t **p);
+isc_quota_release(isc_quota_t *quota);
/*%<
- * Release one unit of quota, and also detaches '*p' from the quota.
+ * Release one unit of quota.
*/
ISC_LANG_ENDDECLS
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
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
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);
.active_link = ISC_LINK_INITIALIZER,
.active = true,
.job = ISC_JOB_INITIALIZER,
+ .quotacb = ISC_JOB_INITIALIZER,
};
if (iface != NULL) {
}
}
-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) {
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) {
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;
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;
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());
}
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
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());
-
- if (isc__nmsocket_closing(ssock)) {
- if (ssock->pquota != NULL) {
- isc_quota_detach(&ssock->pquota);
- }
-
- return (ISC_R_CANCELED);
- }
+ REQUIRE(VALID_NMSOCK(csock));
+ REQUIRE(VALID_NMSOCK(csock->server));
+ REQUIRE(csock->tid == isc_tid());
- 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;
-
- worker = csock->worker;
+ 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);
- 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;
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;
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
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);
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);
}
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
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);
#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
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);
- atomic_fetch_sub_relaxed("a->waiting, 1);
+ 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;
/*
*/
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);
- }
+ cb = dequeue(quota);
UNLOCK("a->cblock);
if (cb != NULL) {
- cb->cb_func(quota, cb->data);
+ cb->cb(cb->cbarg);
return;
}
}
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) {
- REQUIRE(VALID_QUOTA(quota));
- REQUIRE(quotap != NULL && *quotap == NULL);
-
- return (isc_quota_attach_cb(quota, quotap, NULL));
-}
-
isc_result_t
-isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **quotap,
- isc_quota_cb_t *cb) {
+isc_quota_acquire_cb(isc_quota_t *quota, isc_job_t *job, isc_job_cb cb,
+ void *cbarg) {
REQUIRE(VALID_QUOTA(quota));
- REQUIRE(cb == NULL || VALID_QUOTA_CB(cb));
- REQUIRE(quotap != NULL && *quotap == NULL);
+ REQUIRE(job == NULL || cb != NULL);
+
+ uint_fast32_t used = atomic_fetch_add_release("a->used, 1);
+
+ 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, job);
+ UNLOCK("a->cblock);
+ }
+ return (ISC_R_QUOTA);
+ }
- isc_result_t result = doattach(quota, quotap);
- if (result == ISC_R_QUOTA && cb != NULL) {
- LOCK("a->cblock);
- enqueue(quota, cb);
- UNLOCK("a->cblock);
+ uint_fast32_t soft = atomic_load_relaxed("a->soft);
+ if (soft != 0 && used >= soft) {
+ return (ISC_R_SOFTQUOTA);
}
- return (result);
-}
-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;
+ return (ISC_R_SUCCESS);
}
void
-isc_quota_detach(isc_quota_t **quotap) {
- REQUIRE(quotap != NULL && VALID_QUOTA(*quotap));
- isc_quota_t *quota = *quotap;
- *quotap = NULL;
+isc_quota_destroy(isc_quota_t *quota) {
+ REQUIRE(VALID_QUOTA(quota));
+ quota->magic = 0;
- 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);
}
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
*/
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);
#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
struct {
isc_nmhandle_t *handle;
dns_fetch_t *fetch;
- isc_quota_t *quota;
} recursions[RECTYPE_COUNT];
ns_query_recparam_t recparam;
}
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;
break;
}
- isc_quota_detach(quotap);
+ isc_quota_release(&client->manager->sctx->recursionquota);
FALLTHROUGH;
default:
return (result);
}
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);
}
stale_refresh_aftermath(client, result);
}
- recursionquotatype_detach(client, recursion_type);
+ recursionquotatype_detach(client);
free_fresp(client, &resp);
isc_nmhandle_detach(handlep);
}
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;
}
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
isc_nmhandle_detach(handlep);
- recursionquotatype_detach(client, recursion_type);
+ recursionquotatype_detach(client);
}
}
* the manager's recursing-clients list.
*/
- recursionquotatype_detach(client, RECTYPE_NORMAL);
+ recursionquotatype_detach(client);
LOCK(&client->manager->reclock);
if (ISC_LINK_LINKED(client, rlink)) {
* 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:
inc_stats(client, ns_statscounter_recursion);
}
- result = check_recursionquota(client, RECTYPE_NORMAL);
+ result = check_recursionquota(client);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (sigrdataset != NULL) {
ns_client_putrdataset(client, &sigrdataset);
}
- recursionquotatype_detach(client, RECTYPE_NORMAL);
+ recursionquotatype_detach(client);
}
/*
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)) {
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;
}
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
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)",
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);
}
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);
}
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);
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));
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 */
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,
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;
/*
* 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;
}
/*
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,
} 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),
xfr->end_serial = current_serial;
xfr->mnemonic = mnemonic;
stream = NULL;
- quota = NULL;
CHECK(xfr->stream->methods->first(xfr->stream));
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);
}
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");
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,
* 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;
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);
}
#define UNIT_TESTING
#include <cmocka.h>
+#include <isc/job.h>
#include <isc/quota.h>
#include <isc/result.h>
#include <isc/thread.h>
#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);
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;
}
}
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);
}
ISC_RUN_TEST_IMPL(isc_quota_soft) {
- isc_quota_t quota;
- isc_quota_t *quotas[110];
int i;
UNUSED(state);
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);
}
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);
* 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
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);
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;
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]);
}
}
ISC_RUN_TEST_IMPL(isc_quota_callback_mt) {
UNUSED(state);
- isc_quota_t quota;
int i;
isc_quota_init("a, 100);
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++) {
.action = ns_test_qctx_destroy_hook,
.action_data = &asdata,
};
- isc_quota_t *quota = NULL;
isc_statscounter_t srvfail_cnt;
bool expect_servfail = false;
*/
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);
}
/* 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);
*/
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);
}
}