]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor the isc_quota code and fix the quota in TCP accept code
authorOndřej Surý <ondrej@isc.org>
Tue, 11 Apr 2023 05:54:58 +0000 (07:54 +0200)
committerOndřej Surý <ondrej@isc.org>
Wed, 12 Apr 2023 12:10:37 +0000 (14:10 +0200)
In e18541287231b721c9cdb7e492697a2a80fd83fc, 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.

13 files changed:
lib/isc/include/isc/quota.h
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/tcp.c
lib/isc/netmgr/udp.c
lib/isc/quota.c
lib/ns/client.c
lib/ns/include/ns/query.h
lib/ns/query.c
lib/ns/update.c
lib/ns/xfrout.c
tests/isc/quota_test.c
tests/ns/query_test.c

index 2a25fa191f31be2267e3ae7676d4208c5c18847b..22e653345c01f145089b95589d43df53b2adcdca 100644 (file)
  ***/
 
 #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);
-/*%<
- * 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
index 708df672740263624ca1fe37a97b90e99d3c0853..25bc5443960efe74b17bf72d79ce2a4272004051 100644 (file)
@@ -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
index 5862ee4c13ab8da7f153ffe88571410f302bb514..aea9261e5ec32eeb702b14d63d96d5f47385dd8c 100644 (file)
@@ -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) {
index 670db30579475759f8775e5e85becf959f8d6f0a..7b70fb8688c21ddc8f4dcfd4fa63aa93df7d48fa 100644 (file)
@@ -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, &quota,
-                                            &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());
-
-       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;
@@ -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
index 5fea2e8f33134bf3f620925883622365c89cd9d5..44de0fa5158f946c09176006d9add5d23d657098 100644 (file)
@@ -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);
index 5465db5b73bd346b6ea6ec7b515709f74bd576ea..4dbdbc5781629205c5191ef02f28a200fec375ca 100644 (file)
 #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(&quota->max, max);
        atomic_init(&quota->used, 0);
        atomic_init(&quota->soft, 0);
        atomic_init(&quota->waiting, 0);
-       ISC_LIST_INIT(quota->cbs);
+       ISC_LIST_INIT(quota->jobs);
        isc_mutex_init(&quota->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(&quota->used) == 0);
-       INSIST(atomic_load(&quota->waiting) == 0);
-       INSIST(ISC_LIST_EMPTY(quota->cbs));
-       atomic_store_release(&quota->max, 0);
-       atomic_store_release(&quota->used, 0);
-       atomic_store_release(&quota->soft, 0);
-       isc_mutex_destroy(&quota->cblock);
-}
-
 void
 isc_quota_soft(isc_quota_t *quota, unsigned int soft) {
        REQUIRE(VALID_QUOTA(quota));
-       atomic_store_release(&quota->soft, soft);
+       atomic_store_relaxed(&quota->soft, soft);
 }
 
 void
 isc_quota_max(isc_quota_t *quota, unsigned int max) {
        REQUIRE(VALID_QUOTA(quota));
-       atomic_store_release(&quota->max, max);
+       atomic_store_relaxed(&quota->max, max);
 }
 
 unsigned int
@@ -81,46 +64,27 @@ isc_quota_getused(isc_quota_t *quota) {
        return (atomic_load_relaxed(&quota->used));
 }
 
-static isc_result_t
-quota_reserve(isc_quota_t *quota) {
-       isc_result_t result;
-       uint_fast32_t max = atomic_load_acquire(&quota->max);
-       uint_fast32_t soft = atomic_load_acquire(&quota->soft);
-       uint_fast32_t used = atomic_load_acquire(&quota->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(&quota->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(&quota->waiting, 1);
+       ISC_LIST_ENQUEUE(quota->jobs, cb, link);
+       atomic_fetch_add_relaxed(&quota->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(&quota->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(&quota->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(&quota->waiting) > 0) {
-               isc_quota_cb_t *cb = NULL;
+               isc_job_t *cb = NULL;
                LOCK(&quota->cblock);
-               if (atomic_load_relaxed(&quota->waiting) > 0) {
-                       cb = dequeue(quota);
-               }
+               cb = dequeue(quota);
                UNLOCK(&quota->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) {
-       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(&quota->used, 1);
+
+       uint_fast32_t max = atomic_load_relaxed(&quota->max);
+       if (max != 0 && used >= max) {
+               (void)atomic_fetch_sub_relaxed(&quota->used, 1);
+               if (job != NULL) {
+                       job->cb = cb;
+                       job->cbarg = cbarg;
+                       LOCK(&quota->cblock);
+                       enqueue(quota, job);
+                       UNLOCK(&quota->cblock);
+               }
+               return (ISC_R_QUOTA);
+       }
 
-       isc_result_t result = doattach(quota, quotap);
-       if (result == ISC_R_QUOTA && cb != NULL) {
-               LOCK(&quota->cblock);
-               enqueue(quota, cb);
-               UNLOCK(&quota->cblock);
+       uint_fast32_t soft = atomic_load_relaxed(&quota->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(&quota->used) == 0);
+       INSIST(atomic_load(&quota->waiting) == 0);
+       INSIST(ISC_LIST_EMPTY(quota->jobs));
+       isc_mutex_destroy(&quota->cblock);
 }
index 37a08b3210ff9f3e245050be1f9ad3468445c807..5e6f14f780d70c4427d2554ec746adc1419ddb1a 100644 (file)
@@ -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);
index abb18fbd99559e82354edf455db94ba4a07e57cf..96bcbf9b396ecc7f3defd1440498058c636d806d 100644 (file)
@@ -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;
index 83413ec0ece30b04f4a93ed2a46bcd021a37018d..5d223e54832aa49a76e60373f8c9cd5e56692ac7 100644 (file)
@@ -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
index e99afc1b01d9dc76de06cd83eb1f3bcc3dbdcb95..bd3d3af3d404df32f38957a86c441a6dcbf37144 100644 (file)
@@ -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));
index b42c08cc6a7b5aa2d4a2c3e213d583d56e773a7a..041c6a109ff717ed4870320bae3418540d9dc205 100644 (file)
@@ -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, &quota);
+       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(&quota);
-       }
        if (current_soa_tuple != NULL) {
                dns_difftuple_free(&current_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);
        }
index d4ae843a4a402ad652bd2655db7a98164ae56caa..cbf2724ea2991659c0d9614e00ad2bb84f455482 100644 (file)
@@ -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>
 
 #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(&quota, 100);
 
        assert_int_equal(isc_quota_getmax(&quota), 100);
@@ -47,30 +48,26 @@ ISC_RUN_TEST_IMPL(isc_quota_get_set) {
        assert_int_equal(isc_quota_getsoft(&quota), 30);
 
        assert_int_equal(isc_quota_getused(&quota), 0);
-       isc_quota_attach(&quota, &quota2);
+       isc_quota_acquire(&quota);
        assert_int_equal(isc_quota_getused(&quota), 1);
-       isc_quota_detach(&quota2);
+       isc_quota_release(&quota);
        assert_int_equal(isc_quota_getused(&quota), 0);
        isc_quota_destroy(&quota);
 }
 
 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(&quota, 100);
 
        for (i = 0; i < 100; i++) {
-               add_quota(&quota, &quotas[i], ISC_R_SUCCESS, i + 1);
+               add_quota(&quota, ISC_R_SUCCESS, i + 1);
        }
 
-       add_quota(&quota, &quotas[100], ISC_R_QUOTA, 100);
+       add_quota(&quota, ISC_R_QUOTA, 100);
 
        assert_int_equal(isc_quota_getused(&quota), 100);
 
-       isc_quota_detach(&quotas[0]);
-       assert_null(quotas[0]);
+       isc_quota_release(&quota);
 
-       add_quota(&quota, &quotas[100], ISC_R_SUCCESS, 100);
-       add_quota(&quota, &quotas[101], ISC_R_QUOTA, 100);
+       add_quota(&quota, ISC_R_SUCCESS, 100);
+       add_quota(&quota, ISC_R_QUOTA, 100);
 
        for (i = 100; i > 0; i--) {
-               isc_quota_detach(&quotas[i]);
-               assert_null(quotas[i]);
+               isc_quota_release(&quota);
                assert_int_equal(isc_quota_getused(&quota), i - 1);
        }
        assert_int_equal(isc_quota_getused(&quota), 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(&quota, 50);
 
        for (i = 0; i < 50; i++) {
-               add_quota(&quota, &quotas[i], ISC_R_SUCCESS, i + 1);
+               add_quota(&quota, ISC_R_SUCCESS, i + 1);
        }
        for (i = 50; i < 100; i++) {
-               add_quota(&quota, &quotas[i], ISC_R_SOFTQUOTA, i + 1);
+               add_quota(&quota, ISC_R_SOFTQUOTA, i + 1);
        }
 
-       add_quota(&quota, &quotas[i], ISC_R_QUOTA, 100);
+       add_quota(&quota, ISC_R_QUOTA, 100);
 
        for (i = 99; i >= 0; i--) {
-               isc_quota_detach(&quotas[i]);
-               assert_null(quotas[i]);
+               isc_quota_release(&quota);
                assert_int_equal(isc_quota_getused(&quota), i);
        }
        assert_int_equal(isc_quota_getused(&quota), 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(&quota);
+               isc_quota_release(&quota);
        }
 }
 
 ISC_RUN_TEST_IMPL(isc_quota_callback) {
        isc_result_t result;
-       isc_quota_t quota;
-       isc_quota_t *quotas[30];
-       qp = &quota;
        /*
         * - 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(&quota, 10);
 
        for (i = 0; i < 10; i++) {
-               quotas[i] = NULL;
-               isc_quota_cb_init(&cbs[i], callback, &ints[i]);
-               result = isc_quota_attach_cb(&quota, &quotas[i], &cbs[i]);
+               cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
+               result = isc_quota_acquire_cb(&quota, &cbs[i], callback,
+                                             &ints[i]);
                assert_int_equal(result, ISC_R_SUCCESS);
-               assert_ptr_equal(quotas[i], &quota);
                assert_int_equal(isc_quota_getused(&quota), 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(&quota, &quotas[i], &cbs[i]);
+               cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
+               result = isc_quota_acquire_cb(&quota, &cbs[i], callback,
+                                             &ints[i]);
                assert_int_equal(result, ISC_R_SOFTQUOTA);
-               assert_ptr_equal(quotas[i], &quota);
                assert_int_equal(isc_quota_getused(&quota), 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(&quota, &quotas[i], &cbs[i]);
+               cbs[i] = (isc_job_t)ISC_JOB_INITIALIZER;
+               result = isc_quota_acquire_cb(&quota, &cbs[i], callback,
+                                             &ints[i]);
                assert_int_equal(result, ISC_R_QUOTA);
-               assert_ptr_equal(quotas[i], NULL);
                assert_int_equal(isc_quota_getused(&quota), 20);
        }
        assert_int_equal(atomic_load(&cb_calls), 0);
 
        for (i = 0; i < 5; i++) {
-               isc_quota_detach(&quotas[i]);
-               assert_null(quotas[i]);
+               isc_quota_release(&quota);
                assert_int_equal(isc_quota_getused(&quota), 20);
                assert_int_equal(atomic_load(&cb_calls), i + 1);
        }
        /* That should cause a chain reaction */
-       isc_quota_detach(&quotas[5]);
+       isc_quota_release(&quota);
        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 = &quota;
-               isc_quota_detach(&qp);
+               isc_quota_release(&quota);
        }
 
        for (i = 6; i < 20; i++) {
-               isc_quota_detach(&quotas[i]);
-               assert_null(quotas[i]);
+               isc_quota_release(&quota);
                assert_int_equal(isc_quota_getused(&quota), 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(&quota);
+       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, &quota, &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, &quota,
-                                                         &qti->callbacks[i]);
+               qti->callbacks[i] = (isc_job_t)ISC_JOB_INITIALIZER;
+               isc_result_t result = isc_quota_acquire_cb(
+                       &quota, &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, &quota,
                                          &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(&quota, 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 = &quota;
                isc_thread_create(quota_thread, &qtis[i], &threads[i]);
        }
        for (i = 0; i < 10; i++) {
index 2c538c432088524a28baf64e1fb3c43cd1358829..6dcf5f102fbcb60d4c2704b17e6318eb37cb3a3c 100644 (file)
@@ -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, &quota);
+               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(&quota);
+       if (!test->quota_ok) {
+               isc_quota_release(&sctx->recursionquota);
        }
 }