diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index e22e40d265..4ae8cfa75b 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.h,v 1.35 2000/08/01 01:12:16 tale Exp $ */ +/* $Id: server.h,v 1.36 2000/09/05 03:35:16 marka Exp $ */ #ifndef NAMED_SERVER_H #define NAMED_SERVER_H 1 @@ -53,6 +53,7 @@ struct ns_server { dns_aclenv_t aclenv; /* Server data structures. */ + dns_loadmgr_t * loadmgr; dns_zonemgr_t * zonemgr; ns_clientmgr_t * clientmgr; dns_viewlist_t viewlist; diff --git a/bin/named/server.c b/bin/named/server.c index a4f5d32d35..ed54b362b9 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.217 2000/08/31 12:15:07 marka Exp $ */ +/* $Id: server.c,v 1.218 2000/09/05 03:35:14 marka Exp $ */ #include @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -429,6 +430,11 @@ configure_view(dns_view_t *view, dns_c_ctx_t *cctx, dns_c_view_t *cview, port = 53; dns_view_setdstport(view, port); + /* + * Attach load manager to view. + */ + dns_view_setloadmgr(view, ns_g_server->loadmgr); + /* * Configure the view's cache. Try to reuse an existing * cache if possible, otherwise create a new cache. @@ -1315,6 +1321,8 @@ load_configuration(const char *filename, ns_server_t *server, configure_server_quota(cctx, dns_c_ctx_getrecursiveclients, &server->recursionquota, 1000); + /* dns_loadmgr_setlimit(server->loadmgr, 20); XXXMPA */ + /* * Configure the zone manager. */ @@ -1725,6 +1733,7 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { RUNTIME_CHECK(result == ISC_R_SUCCESS); /* Initialize server data structures. */ + server->loadmgr = NULL; server->zonemgr = NULL; server->clientmgr = NULL; server->interfacemgr = NULL; @@ -1774,6 +1783,8 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr, ns_g_socketmgr, &server->zonemgr), "dns_zonemgr_create"); + CHECKFATAL(dns_loadmgr_create(ns_g_mctx, &server->loadmgr), + "dns_loadmgr_create"); server->magic = NS_SERVER_MAGIC; *serverp = server; @@ -1784,6 +1795,7 @@ ns_server_destroy(ns_server_t **serverp) { ns_server_t *server = *serverp; REQUIRE(NS_SERVER_VALID(server)); + dns_loadmgr_detach(&server->loadmgr); dns_zonemgr_detach(&server->zonemgr); if (server->tkeyctx != NULL) diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h index 7e4778df45..d1d7d0a827 100644 --- a/lib/dns/include/dns/events.h +++ b/lib/dns/include/dns/events.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: events.h,v 1.30 2000/08/31 12:15:14 marka Exp $ */ +/* $Id: events.h,v 1.31 2000/09/05 03:35:21 marka Exp $ */ #ifndef DNS_EVENTS_H #define DNS_EVENTS_H 1 @@ -54,6 +54,7 @@ #define DNS_EVENT_ZONESTARTXFRIN (ISC_EVENTCLASS_DNS + 25) #define DNS_EVENT_MASTERQUANTUM (ISC_EVENTCLASS_DNS + 26) #define DNS_EVENT_CACHEOVERMEM (ISC_EVENTCLASS_DNS + 27) +#define DNS_EVENT_MASTERNEXTZONE (ISC_EVENTCLASS_DNS + 28) #define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0) #define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535) diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h index c74b770564..d9a451b2b8 100644 --- a/lib/dns/include/dns/master.h +++ b/lib/dns/include/dns/master.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.h,v 1.23 2000/08/15 03:33:51 marka Exp $ */ +/* $Id: master.h,v 1.24 2000/09/05 03:35:22 marka Exp $ */ #ifndef DNS_MASTER_H #define DNS_MASTER_H 1 @@ -96,6 +96,15 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, dns_loaddonefunc_t done, void *done_arg, isc_mem_t *mctx); +isc_result_t +dns_master_loadfilequota(const char *master_file, dns_name_t *top, + dns_name_t *origin, dns_rdataclass_t zclass, + isc_boolean_t age_ttl, dns_rdatacallbacks_t *callbacks, + isc_task_t *task, dns_loaddonefunc_t done, + void *done_arg, dns_loadmgr_t *lmgr, + dns_loadctx_t **ctxp, isc_mem_t *mctx); + + /* * Loads a RFC 1305 master file from a file, stream, or buffer into rdatasets * and then calls 'callbacks->commit' to commit the rdatasets. Rdata memory @@ -122,6 +131,8 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, * 'callbacks->done' points to a valid function or NULL. * 'mctx' points to a valid memory context. * 'task' and 'done' to be NULL or 'task' and 'done' to be valid. + * 'lmgr' to be valid. + * 'ctxp != NULL && ctxp == NULL'. * * Returns: * ISC_R_SUCCESS upon successfully loading the master file. @@ -137,6 +148,103 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, * Any error code from callbacks->commit(). */ +void +dns_loadctx_detach(dns_loadctx_t **ctxp); +/* + * Detach from the load context. + * + * Requires: + * '*ctxp' to be valid. + * + * Ensures: + * '*ctxp == NULL' + */ + +void +dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target); +/* + * Attach to the load context. + * + * Requires: + * 'source' to be valid. + * 'target != NULL && *target == NULL'. + */ + +void +dns_loadctx_cancel(dns_loadctx_t *ctx); +/* + * Cancel loading the zone file associated with this load context. + * + * Requires: + * 'ctx' to be valid + */ + +isc_result_t +dns_loadmgr_create(isc_mem_t *mctx, dns_loadmgr_t **mgrp); +/* + * Create a new load manager. + * + * Requires: + * 'mgrp != NULL && *mgrp == NULL' + * + * Returns: + * ISC_R_SUCCESS upon successfully creating a load manager. + * ISC_R_MEMORY + * ISC_R_UNEXPECTED + */ + +void +dns_loadmgr_cancel(dns_loadmgr_t *mgr); +/* + * Cancel all queue loads. Loads that are already in progress are not + * canceled. + * + * Requires: + * 'mgr' to be valid. + */ + +void +dns_loadmgr_attach(dns_loadmgr_t *source, dns_loadmgr_t **target); +/* + * Attach to the load manager. + * + * Requires: + * 'source' to be valid. + * 'target != NULL && *target == NULL' + */ + +void +dns_loadmgr_detach(dns_loadmgr_t **mgrp); +/* + * Detach from the load manager. + * + * Requires: + * '*mgrp' to be valid. + * + * Ensures: + * '*mgr == NULL' + */ + +void +dns_loadmgr_setlimit(dns_loadmgr_t *mgr, isc_uint32_t limit); +/* + * Set the number of simultaneous loads permitted by the load manager. + * 0 is unlimited. + * + * Requires: + * 'mgr' to be valid. + */ + +isc_uint32_t +dns_loadmgr_getlimit(dns_loadmgr_t *mgr); +/* + * Return the number of simultaneous loads permitted by the load manager. + * 0 is unlimited. + * + * Requires: + * 'mgr' to be valid. + */ + ISC_LANG_ENDDECLS #endif /* DNS_MASTER_H */ diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 8ff5886096..dcc99c8f7a 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: types.h,v 1.93 2000/08/24 22:15:37 bwelling Exp $ */ +/* $Id: types.h,v 1.94 2000/09/05 03:35:23 marka Exp $ */ #ifndef DNS_TYPES_H #define DNS_TYPES_H 1 @@ -64,6 +64,8 @@ typedef isc_uint16_t dns_keyflags_t; typedef struct dns_keynode dns_keynode_t; typedef struct dns_keytable dns_keytable_t; typedef isc_uint16_t dns_keytag_t; +typedef struct dns_loadctx dns_loadctx_t; +typedef struct dns_loadmgr dns_loadmgr_t; typedef struct dns_message dns_message_t; typedef isc_uint16_t dns_messageid_t; typedef isc_region_t dns_label_t; diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 0f6124f38c..df3883468c 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.h,v 1.51 2000/08/25 01:16:04 bwelling Exp $ */ +/* $Id: view.h,v 1.52 2000/09/05 03:35:24 marka Exp $ */ #ifndef DNS_VIEW_H #define DNS_VIEW_H 1 @@ -80,6 +80,7 @@ struct dns_view { dns_zt_t * zonetable; dns_resolver_t * resolver; dns_adb_t * adb; + dns_loadmgr_t * loadmgr; dns_requestmgr_t * requestmgr; dns_cache_t * cache; dns_db_t * cachedb; @@ -600,6 +601,9 @@ dns_view_checksig(dns_view_t *view, isc_buffer_t *source, dns_message_t *msg); * see dns_tsig_verify() */ +void +dns_view_setloadmgr(dns_view_t *view, dns_loadmgr_t *loadmgr); + ISC_LANG_ENDDECLS #endif /* DNS_VIEW_H */ diff --git a/lib/dns/master.c b/lib/dns/master.c index e5eb3f92bf..6fa5a7a25f 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.c,v 1.63 2000/08/24 19:13:13 gson Exp $ */ +/* $Id: master.c,v 1.64 2000/09/05 03:35:17 marka Exp $ */ #include @@ -82,13 +82,12 @@ typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t; /* * Master file loading state that persists across $INCLUDEs. */ -typedef struct loadctx loadctx_t; -struct loadctx { +struct dns_loadctx { isc_uint32_t magic; isc_mem_t *mctx; isc_lex_t *lex; - loadctx_t *parent; + dns_loadctx_t *parent; dns_rdatacallbacks_t *callbacks; isc_task_t *task; dns_loaddonefunc_t done; @@ -113,13 +112,38 @@ struct loadctx { int origin_in_use; unsigned int loop_cnt; /* records per quantum, * 0 => all. */ + isc_boolean_t canceled; + /* Rate limit goo. */ + isc_boolean_t rate_limited; + ISC_LINK(dns_loadctx_t) link; + char *master_file; + dns_loadmgr_t *loadmgr; + isc_event_t event; + + isc_mutex_t lock; + /* locked by lock */ + isc_uint32_t references; }; #define DNS_LCTX_MAGIC ISC_MAGIC('L','c','t','x') #define DNS_LCTX_VALID(ctx) ISC_MAGIC_VALID(ctx, DNS_LCTX_MAGIC) +struct dns_loadmgr { + isc_uint32_t magic; + isc_mem_t *mctx; + isc_uint32_t erefs; + isc_uint32_t irefs; + isc_mutex_t lock; + isc_uint32_t active; + isc_uint32_t limit; + ISC_LIST(dns_loadctx_t) list; +}; + +#define DNS_LMGR_MAGIC ISC_MAGIC('L','m','g','r') +#define DNS_LMGR_VALID(ctx) ISC_MAGIC_VALID(ctx, DNS_LMGR_MAGIC) + static isc_result_t -pushfile(const char *master_file, dns_name_t *origin, loadctx_t **ctxp); +pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t **ctxp); static isc_result_t commit(dns_rdatacallbacks_t *, isc_lex_t *, rdatalist_head_t *, dns_name_t *, @@ -143,7 +167,25 @@ static void load_quantum(isc_task_t *task, isc_event_t *event); static isc_result_t -task_send(loadctx_t *ctx); +task_send(dns_loadctx_t *ctx); + +static void +loadctx_destroy(dns_loadctx_t *ctx); + +static void +loadmgr_start(isc_task_t *task, isc_event_t *event); + +static void +loadmgr_cancel(dns_loadmgr_t *mgr); + +static void +loadmgr_iattach(dns_loadmgr_t *source, dns_loadmgr_t **target); + +static void +loadmgr_idetach(dns_loadmgr_t **mgrp); + +static void +loadmgr_destroy(dns_loadmgr_t *mgr); #define GETTOKEN(lexer, options, token, eol) \ do { \ @@ -224,20 +266,52 @@ gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token, return (ISC_R_SUCCESS); } -static void -loadctx_destroy(loadctx_t **ctxp) { - loadctx_t *ctx; - isc_mem_t *mctx; + +void +dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target) { + + REQUIRE(target != NULL && *target == NULL); + REQUIRE(DNS_LCTX_VALID(source)); + + LOCK(&source->lock); + INSIST(source->references > 0); + source->references++; + INSIST(source->references != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +void +dns_loadctx_detach(dns_loadctx_t **ctxp) { + dns_loadctx_t *ctx; + isc_boolean_t need_destroy = ISC_FALSE; REQUIRE(ctxp != NULL); ctx = *ctxp; REQUIRE(DNS_LCTX_VALID(ctx)); + + LOCK(&ctx->lock); + INSIST(ctx->references > 0); + ctx->references--; + if (ctx->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&ctx->lock); + if (need_destroy) + loadctx_destroy(ctx); *ctxp = NULL; +} + +static void +loadctx_destroy(dns_loadctx_t *ctx) { + isc_mem_t *mctx; + + REQUIRE(DNS_LCTX_VALID(ctx)); ctx->magic = 0; if (ctx->parent != NULL) - loadctx_destroy(&ctx->parent); + dns_loadctx_detach(&ctx->parent); if (ctx->lex != NULL) { isc_lex_close(ctx->lex); @@ -245,6 +319,13 @@ loadctx_destroy(loadctx_t **ctxp) { } if (ctx->task != NULL) isc_task_detach(&ctx->task); + if (ctx->master_file != NULL) { + isc_mem_free(ctx->mctx, ctx->master_file); + ctx->master_file = NULL; + } + if (ctx->loadmgr != NULL) + loadmgr_idetach(&ctx->loadmgr); + DESTROYLOCK(&ctx->lock); mctx = NULL; isc_mem_attach(ctx->mctx, &mctx); isc_mem_detach(&ctx->mctx); @@ -257,9 +338,9 @@ loadctx_create(isc_mem_t *mctx, isc_boolean_t age_ttl, dns_name_t *top, dns_rdataclass_t zclass, dns_name_t *origin, dns_rdatacallbacks_t *callbacks, isc_task_t *task, dns_loaddonefunc_t done, void *done_arg, - loadctx_t **ctxp) + dns_loadctx_t **ctxp) { - loadctx_t *ctx; + dns_loadctx_t *ctx; isc_result_t result; isc_region_t r; int i; @@ -279,6 +360,14 @@ loadctx_create(isc_mem_t *mctx, isc_boolean_t age_ttl, dns_name_t *top, ctx = isc_mem_get(mctx, sizeof(*ctx)); if (ctx == NULL) return (ISC_R_NOMEMORY); + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, ctx, sizeof *ctx); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } ctx->lex = NULL; result = isc_lex_create(mctx, TOKENSIZ, &ctx->lex); @@ -328,8 +417,18 @@ loadctx_create(isc_mem_t *mctx, isc_boolean_t age_ttl, dns_name_t *top, ctx->done = done; ctx->done_arg = done_arg; + ctx->rate_limited = ISC_FALSE; + ctx->master_file = NULL; + ctx->loadmgr = NULL; + ISC_LINK_INIT(ctx, link); + ISC_EVENT_INIT(&ctx->event, sizeof(ctx->event), 0, NULL, + DNS_EVENT_MASTERNEXTZONE, loadmgr_start, + ctx, ctx, NULL, NULL); + + ctx->canceled = ISC_FALSE; ctx->mctx = NULL; isc_mem_attach(mctx, &ctx->mctx); + ctx->references = 1; /* Implicit attach. */ ctx->magic = DNS_LCTX_MAGIC; *ctxp = ctx; return (ISC_R_SUCCESS); @@ -340,7 +439,7 @@ loadctx_create(isc_mem_t *mctx, isc_boolean_t age_ttl, dns_name_t *top, } static isc_result_t -load(loadctx_t **ctxp) { +load(dns_loadctx_t **ctxp) { dns_rdataclass_t rdclass; dns_rdatatype_t type, covers; isc_uint32_t ttl_offset = 0; @@ -376,7 +475,7 @@ load(loadctx_t **ctxp) { unsigned int loop_cnt = 0; isc_mem_t *mctx; dns_rdatacallbacks_t *callbacks; - loadctx_t *ctx; + dns_loadctx_t *ctx; ctx = *ctxp; @@ -416,7 +515,7 @@ load(loadctx_t **ctxp) { CTX_COPYVAR(ctx, *ctxp, ttl); CTX_COPYVAR(ctx, *ctxp, default_ttl); CTX_COPYVAR(ctx, *ctxp, warn_1035); - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); ctx = *ctxp; continue; } @@ -993,10 +1092,10 @@ load(loadctx_t **ctxp) { } static isc_result_t -pushfile(const char *master_file, dns_name_t *origin, loadctx_t **ctxp) { +pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t **ctxp) { isc_result_t result; - loadctx_t *ctx; - loadctx_t *new = NULL; + dns_loadctx_t *ctx; + dns_loadctx_t *new = NULL; isc_region_t r; int new_in_use; @@ -1043,7 +1142,7 @@ pushfile(const char *master_file, dns_name_t *origin, loadctx_t **ctxp) { cleanup: if (new != NULL) - loadctx_destroy(&new); + dns_loadctx_detach(&new); return (result); } @@ -1053,7 +1152,7 @@ dns_master_loadfile(const char *master_file, dns_name_t *top, dns_rdataclass_t zclass, isc_boolean_t age_ttl, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; isc_result_t result; result = loadctx_create(mctx, age_ttl, top, zclass, origin, @@ -1070,10 +1169,110 @@ dns_master_loadfile(const char *master_file, dns_name_t *top, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } +isc_result_t +dns_master_loadfilequota(const char *master_file, dns_name_t *top, + dns_name_t *origin, dns_rdataclass_t zclass, + isc_boolean_t age_ttl, dns_rdatacallbacks_t *callbacks, isc_task_t *task, dns_loaddonefunc_t done, + void *done_arg, dns_loadmgr_t *lmgr, + dns_loadctx_t **ctxp, isc_mem_t *mctx) +{ + isc_boolean_t queue; + dns_loadctx_t *ctx = NULL; + isc_result_t result; + isc_event_t *event; + + REQUIRE(DNS_LMGR_VALID(lmgr)); + REQUIRE(ctxp != NULL && *ctxp == NULL); + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, task, done, done_arg, &ctx); + if (result != ISC_R_SUCCESS) + goto cleanup; + + ctx->rate_limited = ISC_TRUE; + ctx->master_file = isc_mem_strdup(mctx, master_file); + if (ctx->master_file == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + loadmgr_iattach(lmgr, &ctx->loadmgr); + + LOCK(&lmgr->lock); + lmgr->active++; + queue = ISC_TF((lmgr->limit != 0 && lmgr->active > lmgr->limit)); + if (queue) + ISC_LIST_APPEND(lmgr->list, ctx, link); + INSIST(queue || ISC_LIST_EMPTY(lmgr->list)); + UNLOCK(&lmgr->lock); + + dns_loadctx_attach(ctx, ctxp); + result = DNS_R_CONTINUE; + if (!queue) { + event = &ctx->event; + isc_task_send(ctx->task, &event); + } + return (result); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} + +static void +loadmgr_done(dns_loadctx_t *ctx, isc_result_t result) { + dns_loadctx_t *next; + isc_event_t *event; + + if (ctx->done != NULL) + (ctx->done)(ctx->done_arg, result); + + LOCK(&ctx->loadmgr->lock); + INSIST(ctx->loadmgr->active > 0); + ctx->loadmgr->active--; + /* dequeue */ + next = ISC_LIST_HEAD(ctx->loadmgr->list); + if (next != NULL) + ISC_LIST_UNLINK(ctx->loadmgr->list, next, link); + UNLOCK(&ctx->loadmgr->lock); + if (next != NULL) { + event = &next->event; + isc_task_send(next->task, &event); + } +} + + +static void +loadmgr_start(isc_task_t *task, isc_event_t *event) { + dns_loadctx_t *ctx = event->ev_arg; + isc_result_t result; + + INSIST(task == ctx->task); + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) { + result = ISC_R_CANCELED; + goto done; + } + result = isc_lex_openfile(ctx->lex, ctx->master_file); + if (result == ISC_R_SUCCESS) + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + result = task_send(ctx); + if (result == ISC_R_SUCCESS) + isc_event_free(&event); + return; + } + done: + loadmgr_done(ctx, result); + isc_event_free(&event); + dns_loadctx_detach(&ctx); + return; +} + isc_result_t dns_master_loadfileinc(const char *master_file, dns_name_t *top, dns_name_t *origin, dns_rdataclass_t zclass, @@ -1081,7 +1280,7 @@ dns_master_loadfileinc(const char *master_file, dns_name_t *top, isc_task_t *task, dns_loaddonefunc_t done, void *done_arg, isc_mem_t *mctx) { - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; isc_result_t tresult; isc_result_t result; @@ -1106,7 +1305,7 @@ dns_master_loadfileinc(const char *master_file, dns_name_t *top, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } @@ -1116,7 +1315,7 @@ dns_master_loadstream(FILE *stream, dns_name_t *top, dns_name_t *origin, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { isc_result_t result; - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; REQUIRE(stream != NULL); @@ -1134,7 +1333,7 @@ dns_master_loadstream(FILE *stream, dns_name_t *top, dns_name_t *origin, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } @@ -1147,7 +1346,7 @@ dns_master_loadstreaminc(FILE *stream, dns_name_t *top, dns_name_t *origin, { isc_result_t result; isc_result_t tresult; - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; REQUIRE(stream != NULL); @@ -1172,7 +1371,7 @@ dns_master_loadstreaminc(FILE *stream, dns_name_t *top, dns_name_t *origin, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } @@ -1183,7 +1382,7 @@ dns_master_loadbuffer(isc_buffer_t *buffer, dns_name_t *top, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { isc_result_t result; - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; REQUIRE(buffer != NULL); @@ -1201,7 +1400,7 @@ dns_master_loadbuffer(isc_buffer_t *buffer, dns_name_t *top, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } @@ -1215,7 +1414,7 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, dns_name_t *top, { isc_result_t result; isc_result_t tresult; - loadctx_t *ctx = NULL; + dns_loadctx_t *ctx = NULL; REQUIRE(buffer != NULL); @@ -1240,7 +1439,7 @@ dns_master_loadbufferinc(isc_buffer_t *buffer, dns_name_t *top, cleanup: if (ctx != NULL) - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); return (result); } @@ -1458,30 +1657,36 @@ on_list(dns_rdatalist_t *rdatalist, dns_rdata_t *rdata) { static void load_quantum(isc_task_t *task, isc_event_t *event) { isc_result_t result; - loadctx_t *ctx; + dns_loadctx_t *ctx; REQUIRE(event != NULL); ctx = event->ev_arg; REQUIRE(DNS_LCTX_VALID(ctx)); - result = load(&ctx); + if (ctx->canceled) + result = ISC_R_CANCELED; + else + result = load(&ctx); if (result == DNS_R_CONTINUE) { isc_task_send(task, &event); } else if (result == ISC_R_SUCCESS && ctx->parent) { /* Pop ctx and continue. */ event->ev_arg = ctx->parent; ctx->parent = NULL; - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); isc_task_send(task, &event); } else { - (ctx->done)(ctx->done_arg, result); + if (ctx->rate_limited) + loadmgr_done(ctx, result); + else + (ctx->done)(ctx->done_arg, result); isc_event_free(&event); - loadctx_destroy(&ctx); + dns_loadctx_detach(&ctx); } } static isc_result_t -task_send(loadctx_t *ctx) { +task_send(dns_loadctx_t *ctx) { isc_event_t *event; event = isc_event_allocate(ctx->mctx, NULL, @@ -1492,3 +1697,181 @@ task_send(loadctx_t *ctx) { isc_task_send(ctx->task, &event); return (ISC_R_SUCCESS); } + +/* + * DNS load manager. + */ + +isc_result_t +dns_loadmgr_create(isc_mem_t *mctx, dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_result_t result; + + REQUIRE(mgrp != NULL && *mgrp == NULL); + + mgr = isc_mem_get(mctx, sizeof(*mgr)); + if (mgr == NULL) + return (ISC_R_NOMEMORY); + result = isc_mutex_init(&mgr->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, mgr, sizeof(*mgr)); + return (result); + } + mgr->erefs = 1; + mgr->irefs = 0; + mgr->limit = 0; + mgr->active = 0; + mgr->mctx = NULL; + isc_mem_attach(mctx, &mgr->mctx); + ISC_LIST_INIT(mgr->list); + mgr->magic = DNS_LMGR_MAGIC; + *mgrp = mgr; + return (ISC_R_SUCCESS); +} + +void +dns_loadmgr_setlimit(dns_loadmgr_t *mgr, isc_uint32_t limit) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgr->limit = limit; +} + +isc_uint32_t +dns_loadmgr_getlimit(dns_loadmgr_t *mgr) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + return(mgr->limit); +} + +void +dns_loadmgr_cancel(dns_loadmgr_t *mgr) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + LOCK(&mgr->lock); + loadmgr_cancel(mgr); + UNLOCK(&mgr->lock); +} + +static void +loadmgr_cancel(dns_loadmgr_t *mgr) { + dns_loadctx_t *ctx; + isc_event_t *event; + + for (ctx = ISC_LIST_HEAD(mgr->list); ctx != NULL; ) { + ISC_LIST_UNLINK(mgr->list, ctx, link); + event = &ctx->event; + event->ev_attributes |= ISC_EVENTATTR_CANCELED; + isc_task_send(ctx->task, &event); + } +} + +void +dns_loadctx_cancel(dns_loadctx_t *ctx) { + isc_event_t *event; + + REQUIRE(DNS_LCTX_VALID(ctx)); + + LOCK(&ctx->lock); + ctx->canceled = ISC_TRUE; + /* + * If we are queued to be run dequeue. + */ + if (ctx->loadmgr != NULL && ISC_LINK_LINKED(ctx, link)) { + LOCK(&ctx->loadmgr->lock); + ISC_LIST_UNLINK(ctx->loadmgr->list, ctx, link); + UNLOCK(&ctx->loadmgr->lock); + event = &ctx->event; + event->ev_attributes |= ISC_EVENTATTR_CANCELED; + isc_task_send(ctx->task, &event); + } + UNLOCK(&ctx->lock); +} + + +void +dns_loadmgr_attach(dns_loadmgr_t *source, dns_loadmgr_t **target) { + + REQUIRE(DNS_LMGR_VALID(source)); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&source->lock); + INSIST(source->erefs != 0); + source->erefs++; + INSIST(source->erefs != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +void +dns_loadmgr_detach(dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_boolean_t destroy = ISC_FALSE; + + REQUIRE(mgrp != NULL); + mgr = *mgrp; + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgrp = NULL; + + LOCK(&mgr->lock); + INSIST(mgr->erefs != 0); + mgr->erefs--; + if (mgr->erefs == 0) { + if (mgr->irefs == 0) + destroy = ISC_TRUE; + else + loadmgr_cancel(mgr); + } + UNLOCK(&mgr->lock); + if (destroy) + loadmgr_destroy(mgr); +} + +static void +loadmgr_iattach(dns_loadmgr_t *source, dns_loadmgr_t **target) { + + REQUIRE(DNS_LMGR_VALID(source)); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&source->lock); + source->irefs++; + INSIST(source->irefs != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +static void +loadmgr_idetach(dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_boolean_t destroy = ISC_FALSE; + + REQUIRE(mgrp != NULL); + mgr = *mgrp; + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgrp = NULL; + + LOCK(&mgr->lock); + INSIST(mgr->irefs != 0); + mgr->irefs--; + if (mgr->erefs == 0 && mgr->irefs == 0) + destroy = ISC_TRUE; + UNLOCK(&mgr->lock); + if (destroy) + loadmgr_destroy(mgr); +} + +static void +loadmgr_destroy(dns_loadmgr_t *mgr) { + + INSIST(ISC_LIST_EMPTY(mgr->list)); + + mgr->magic = 0; + DESTROYLOCK(&mgr->lock); + isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr)); +} diff --git a/lib/dns/view.c b/lib/dns/view.c index 5553c816e9..f8a2c46ea6 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: view.c,v 1.78 2000/08/26 01:36:59 bwelling Exp $ */ +/* $Id: view.c,v 1.79 2000/09/05 03:35:18 marka Exp $ */ #include @@ -127,6 +127,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->hints = NULL; view->resolver = NULL; view->adb = NULL; + view->loadmgr = NULL; view->requestmgr = NULL; view->mctx = mctx; view->rdclass = rdclass; @@ -228,6 +229,8 @@ destroy(dns_view_t *view) { dns_adb_detach(&view->adb); if (view->resolver != NULL) dns_resolver_detach(&view->resolver); + if (view->loadmgr != NULL) + dns_loadmgr_detach(&view->loadmgr); if (view->requestmgr != NULL) dns_requestmgr_detach(&view->requestmgr); if (view->task != NULL) @@ -523,6 +526,15 @@ dns_view_setdstport(dns_view_t *view, in_port_t dstport) { view->dstport = dstport; } +void +dns_view_setloadmgr(dns_view_t *view, dns_loadmgr_t *loadmgr) { + REQUIRE(DNS_VIEW_VALID(view)); + + if (view->loadmgr != NULL) + dns_loadmgr_detach(&view->loadmgr); + dns_loadmgr_attach(loadmgr, &view->loadmgr); +} + isc_result_t dns_view_addzone(dns_view_t *view, dns_zone_t *zone) { isc_result_t result; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index c1e04b5be3..e778328901 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.200 2000/08/31 06:16:42 marka Exp $ */ +/* $Id: zone.c,v 1.201 2000/09/05 03:35:19 marka Exp $ */ #include @@ -30,10 +30,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -54,6 +56,7 @@ #define NOTIFY_MAGIC 0x4e746679U /* Ntfy */ #define STUB_MAGIC 0x53747562U /* Stub */ #define ZONEMGR_MAGIC 0x5a6d6772U /* Zmgr */ +#define LOAD_MAGIC ISC_MAGIC('L','o','a','d') #define DNS_ZONE_VALID(zone) \ ISC_MAGIC_VALID(zone, ZONE_MAGIC) @@ -63,6 +66,9 @@ ISC_MAGIC_VALID(stub, STUB_MAGIC) #define DNS_ZONEMGR_VALID(stub) \ ISC_MAGIC_VALID(stub, ZONEMGR_MAGIC) +#define DNS_LOAD_VALID(load) \ + ISC_MAGIC_VALID(load, LOAD_MAGIC) + #define RANGE(a, b, c) (((a) < (b)) ? (b) : ((a) < (c) ? (a) : (c))) @@ -79,6 +85,7 @@ typedef struct dns_notify dns_notify_t; typedef struct dns_stub dns_stub_t; +typedef struct dns_load dns_load_t; struct dns_zone { /* Unlocked */ @@ -146,6 +153,7 @@ struct dns_zone { dns_severity_t check_names; ISC_LIST(dns_notify_t) notifies; dns_request_t *request; + dns_loadctx_t *loadctx; isc_uint32_t maxxfrin; isc_uint32_t maxxfrout; isc_uint32_t idlein; @@ -186,6 +194,7 @@ struct dns_zone { #define DNS_ZONEFLG_NOMASTERS 0x00001000U /* an attempt to refresh a * zone with no masters * occured */ +#define DNS_ZONEFLG_LOADING 0x00002000U /* load from disk in progress */ #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) @@ -246,6 +255,18 @@ struct dns_stub { dns_dbversion_t *version; }; +/* + * Hold load state. + */ +struct dns_load { + isc_int32_t magic; + isc_mem_t *mctx; + dns_zone_t *zone; + dns_db_t *db; + isc_time_t loadtime; + dns_rdatacallbacks_t callbacks; +}; + static isc_result_t zone_settimer(dns_zone_t *, isc_stdtime_t); static void cancel_refresh(dns_zone_t *); @@ -261,7 +282,12 @@ static isc_result_t zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump); static isc_result_t default_journal(dns_zone_t *zone); static void zone_xfrdone(dns_zone_t *zone, isc_result_t result); +static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, + isc_time_t loadtime, isc_result_t result); static void zone_shutdown(isc_task_t *, isc_event_t *); +static void zone_loaddone(void *arg, isc_result_t result); +static isc_result_t zone_startload(dns_db_t *db, dns_zone_t *zone, + isc_time_t loadtime); #if 0 /* ondestroy example */ @@ -389,6 +415,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->xfr_acl = NULL; zone->check_names = dns_severity_ignore; zone->request = NULL; + zone->loadctx = NULL; zone->timer = NULL; zone->idlein = DNS_DEFAULT_IDLEIN; zone->idleout = DNS_DEFAULT_IDLEOUT; @@ -443,6 +470,8 @@ zone_free(dns_zone_t *zone) { isc_timer_detach(&zone->timer); if (zone->request != NULL) dns_request_destroy(&zone->request); /* XXXMPA */ + if (zone->loadctx != NULL) + dns_loadctx_detach(&zone->loadctx); INSIST(zone->statelist == NULL); @@ -708,9 +737,6 @@ dns_zone_getjournal(dns_zone_t *zone) { isc_result_t dns_zone_load(dns_zone_t *zone) { const char me[] = "dns_zone_load"; - unsigned int soacount = 0; - unsigned int nscount = 0; - isc_uint32_t serial, refresh, retry, expire, minimum; isc_result_t result; isc_stdtime_t now; isc_time_t loadtime, filetime; @@ -770,7 +796,88 @@ dns_zone_load(dns_zone_t *zone) { goto cleanup; if (!dns_db_ispersistent(db)) + result = zone_startload(db, zone, loadtime); + + if (result == DNS_R_CONTINUE) { + zone->flags |= DNS_ZONEFLG_LOADING; + result = ISC_R_SUCCESS; + goto cleanup; + } + + result = zone_postload(zone, db, loadtime, result); + + cleanup: + UNLOCK(&zone->lock); + if (db != NULL) + dns_db_detach(&db); + return (result); +} + +static isc_result_t +zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { + dns_load_t *load; + isc_result_t result; + isc_result_t tresult; + + if (zone->view != NULL && zone->view->loadmgr != NULL && + zone->db != NULL) { + load = isc_mem_get(zone->mctx, sizeof(*load)); + if (load == NULL) + return (ISC_R_NOMEMORY); + + load->mctx = NULL; + load->zone = NULL; + load->db = NULL; + load->loadtime = loadtime; + load->magic = LOAD_MAGIC; + + isc_mem_attach(zone->mctx, &load->mctx); + dns_zone_iattach(zone, &load->zone); + dns_db_attach(db, &load->db); + dns_rdatacallbacks_init(&load->callbacks); + result = dns_db_beginload(db, &load->callbacks.add, + &load->callbacks.add_private); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = dns_master_loadfilequota(zone->dbname, + dns_db_origin(db), + dns_db_origin(db), + zone->rdclass, ISC_FALSE, + &load->callbacks, zone->task, + zone_loaddone, load, + zone->view->loadmgr, + &zone->loadctx, zone->mctx); + if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) { + tresult = dns_db_endload(load->db, + &load->callbacks.add_private); + if (result == ISC_R_SUCCESS) + result = tresult; + } + } else { result = dns_db_load(db, zone->dbname); + } + + return (result); + + cleanup: + load->magic = 0; + dns_db_detach(&load->db); + dns_zone_idetach(&load->zone); + isc_mem_detach(&load->mctx); + isc_mem_put(zone->mctx, load, sizeof(*load)); + return (result); +} + +static isc_result_t +zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, + isc_result_t result) +{ + const char me[] = "dns_zone_load"; + unsigned int soacount = 0; + unsigned int nscount = 0; + isc_uint32_t serial, refresh, retry, expire, minimum; + isc_stdtime_t now; /* * Initiate zone transfer? We may need a error code that @@ -780,8 +887,14 @@ dns_zone_load(dns_zone_t *zone) { if (result != ISC_R_SUCCESS) { if (zone->type == dns_zone_slave || zone->type == dns_zone_stub) { - zone_log(zone, me, ISC_LOG_INFO, - "no database file"); + if (result == ISC_R_FILENOTFOUND) + zone_log(zone, me, ISC_LOG_INFO, + "no database file"); + else + zone_log(zone, me, ISC_LOG_ERROR, + "database %s: dns_db_load failed: %s", + zone->dbname, + dns_result_totext(result)); /* Mark the zone for immediate refresh. */ zone->refreshtime = now; result = ISC_R_SUCCESS; @@ -797,6 +910,7 @@ dns_zone_load(dns_zone_t *zone) { "number of nodes in database: %u", dns_db_nodecount(db)); zone->loadtime = loadtime; + isc_stdtime_get(&now); zone_log(zone, me, ISC_LOG_DEBUG(1), "loaded"); @@ -914,11 +1028,10 @@ dns_zone_load(dns_zone_t *zone) { zone->flags |= DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY; } result = ISC_R_SUCCESS; + if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) + (void) zone_settimer(zone, now); cleanup: - UNLOCK(&zone->lock); - if (db != NULL) - dns_db_detach(&db); return (result); } @@ -1579,7 +1692,7 @@ dns_zone_refresh(dns_zone_t *zone) { } zone->flags |= DNS_ZONEFLG_REFRESH; UNLOCK(&zone->lock); - if ((oldflags & DNS_ZONEFLG_REFRESH) != 0) + if ((oldflags & (DNS_ZONEFLG_REFRESH|DNS_ZONEFLG_LOADING)) != 0) return; /* @@ -3111,6 +3224,9 @@ zone_shutdown(isc_task_t *task, isc_event_t *event) { if (zone->request != NULL) dns_request_cancel(zone->request); + if (zone->loadctx != NULL) + dns_loadctx_cancel(zone->loadctx); + notify_cancel(zone); if (zone->timer != NULL) { @@ -3387,6 +3503,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, dns_rdata_t rdata; isc_result_t result; isc_stdtime_t now; + char fromtext[ISC_SOCKADDR_FORMATSIZE]; #ifndef NOMINUM_PUBLIC int match = 0; isc_netaddr_t netaddr; @@ -3415,6 +3532,8 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, * first address to check. Return ISC_R_SUCCESS. */ + isc_sockaddr_format(from, fromtext, sizeof(fromtext)); + /* * We only handle NOTIFY (SOA) at the present. */ @@ -3426,7 +3545,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, UNLOCK(&zone->lock); if (msg->counts[DNS_SECTION_QUESTION] == 0) { zone_log(zone, me, ISC_LOG_NOTICE, - "FORMERR no question"); + "FORMERR no question: %s", fromtext); return (DNS_R_FORMERR); } zone_log(zone, me, ISC_LOG_NOTICE, @@ -3465,7 +3584,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, if (i >= zone->masterscnt) { UNLOCK(&zone->lock); zone_log(zone, me, ISC_LOG_DEBUG(3), - "REFUSED notify from non master"); + "REFUSED notify from non master: %s", fromtext); return (DNS_R_REFUSED); } @@ -3494,7 +3613,8 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, serial = soa.serial; if (isc_serial_le(serial, zone->serial)) { zone_log(zone, me, ISC_LOG_DEBUG(3), - "zone up to date"); + "zone up to date: %s", + fromtext); UNLOCK(&zone->lock); return (ISC_R_SUCCESS); } @@ -3520,7 +3640,8 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, zone->notifyfrom = *from; UNLOCK(&zone->lock); zone_log(zone, me, ISC_LOG_DEBUG(3), - "refresh in progress, refresh check queued"); + "refresh in progress, refresh check queued: %s", + fromtext); return (ISC_R_SUCCESS); } isc_stdtime_get(&now); @@ -3528,7 +3649,8 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, zone->notifyfrom = *from; zone_settimer(zone, now); UNLOCK(&zone->lock); - zone_log(zone, me, ISC_LOG_DEBUG(3), "immediate refresh check queued"); + zone_log(zone, me, ISC_LOG_DEBUG(3), "immediate refresh check queued: %s", + fromtext); return (ISC_R_SUCCESS); } @@ -4081,6 +4203,37 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { queue_soa_query(zone); } +static void +zone_loaddone(void *arg, isc_result_t result) { + static char me[] = "zone_loaddone"; + dns_load_t *load = arg; + dns_zone_t *zone; + isc_result_t tresult; + isc_mem_t *mctx; + + REQUIRE(DNS_LOAD_VALID(load)); + zone = load->zone; + mctx = load->mctx; + + DNS_ENTER; + + tresult = dns_db_endload(load->db, &load->callbacks.add_private); + if (result == ISC_R_SUCCESS) + result = tresult; + + LOCK(&load->zone->lock); + (void)zone_postload(load->zone, load->db, load->loadtime, result); + load->zone->flags &= ~DNS_ZONEFLG_LOADING; + UNLOCK(&load->zone->lock); + + load->magic = 0; + dns_db_detach(&load->db); + dns_loadctx_detach(&zone->loadctx); + dns_zone_idetach(&zone); + isc_mem_put(mctx, load, sizeof (*load)); + isc_mem_detach(&mctx); +} + void dns_zone_getssutable(dns_zone_t *zone, dns_ssutable_t **table) { REQUIRE(DNS_ZONE_VALID(zone));