Commit 22608315 authored by Andreas Gustafsson's avatar Andreas Gustafsson
Browse files

Fixed multiple shutdown cleanup bugs in the zone object. This

involved extensive restructuring of the reference counting of
zones and related objects.

Zones now attach to their views.  To avoid a circular dependency that
would keep views from ever shutting down, this is done using the new
functions dns_view_weakattach() / dns_view_weakdetach() which
guarantee that the view will not be freed but still allow it
to be shut down.

The zones themselves now only have a single reference count, with
similar "weak" semantics.  Managed zones must now be shut down
explicitly by calling dns_zone_shutdown().  To shut down all
zones in a zone table, call dns_zt_shutdown().

The zone manager is now reference counted, weakly. To shut down the
zone manager, you must explicitly call dns_zonemgr_shutdown().
parent bc436be0
......@@ -524,7 +524,8 @@ configure_view(dns_view_t *view, dns_c_ctx_t *cctx, dns_c_view_t *cview,
* there is no "version" configuration option.
*/
static isc_result_t
create_version_view(dns_c_ctx_t *cctx, dns_view_t **viewp) {
create_version_view(dns_c_ctx_t *cctx, dns_zonemgr_t *zmgr, dns_view_t **viewp)
{
isc_result_t result;
dns_db_t *db = NULL;
dns_zone_t *zone = NULL;
......@@ -564,6 +565,7 @@ create_version_view(dns_c_ctx_t *cctx, dns_view_t **viewp) {
CHECK(dns_zone_create(&zone, ns_g_mctx));
CHECK(dns_zone_setorigin(zone, &origin));
CHECK(dns_zonemgr_managezone(zmgr, zone));
CHECK(dns_db_create(ns_g_mctx, "rbt", &origin, ISC_FALSE,
dns_rdataclass_ch, 0, NULL, &db));
......@@ -1253,7 +1255,7 @@ load_configuration(const char *filename, ns_server_t *server,
* Create (or recreate) the version view.
*/
view = NULL;
CHECK(create_version_view(cctx, &view));
CHECK(create_version_view(cctx, server->zonemgr, &view));
ISC_LIST_APPEND(lctx.viewlist, view, link);
view = NULL;
......@@ -1447,9 +1449,12 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
dns_dispatch_detach(&server->querysrc_dispatchv6);
ns_clientmgr_destroy(&server->clientmgr);
isc_timer_detach(&server->interface_timer);
ns_interfacemgr_shutdown(server->interfacemgr);
ns_interfacemgr_detach(&server->interfacemgr);
dns_zonemgr_shutdown(server->zonemgr);
dns_zonemgr_detach(&server->zonemgr);
isc_task_detach(&server->task);
......@@ -1551,9 +1556,6 @@ ns_server_destroy(ns_server_t **serverp) {
INSIST(ISC_LIST_EMPTY(server->viewlist));
dns_zonemgr_destroy(&server->zonemgr);
server->zonemgr = NULL;
dns_db_detach(&server->in_roothints);
dns_aclenv_destroy(&server->aclenv);
......
......@@ -110,6 +110,7 @@ struct dns_view {
dns_acl_t * matchclients;
/* Locked by lock. */
unsigned int references;
unsigned int weakrefs;
unsigned int attributes;
/* Under owner's locking control. */
ISC_LINK(struct dns_view) link;
......@@ -165,6 +166,8 @@ dns_view_attach(dns_view_t *source, dns_view_t **targetp);
* Ensures:
*
* *targetp is attached to source.
*
* While *targetp is attached, the view will not shut down.
*/
void
......@@ -174,15 +177,43 @@ dns_view_detach(dns_view_t **viewp);
*
* Requires:
*
* 'viewp' points to a valid dns_view_t *.
* 'viewp' points to a valid dns_view_t *
*
* Ensures:
*
* *viewp is NULL.
*/
void
dns_view_weakattach(dns_view_t *source, dns_view_t **targetp);
/*
* Weakly attach '*targetp' to 'source'.
*
* Requires:
*
* 'source' is a valid, frozen view.
*
* 'targetp' points to a NULL dns_view_t *.
*
* Ensures:
*
* If '*viewp' is the last reference to the view,
* *targetp is attached to source.
*
* While *targetp is attached, the view will not be freed.
*/
void
dns_view_weakdetach(dns_view_t **targetp);
/*
* Detach '*viewp' from its view.
*
* All resources used by the view will be freed.
* Requires:
*
* 'viewp' points to a valid dns_view_t *.
*
* Ensures:
*
* *viewp is NULL.
*/
isc_result_t
......
......@@ -200,30 +200,6 @@ dns_zone_detach(dns_zone_t **zonep);
* 'zonep' to point to a valid initalised zone.
*/
void
dns_zone_iattach(dns_zone_t *source, dns_zone_t **target);
/*
* Attach 'zone' to 'target' incrementing its internal
* reference count. This is intended for use by operations
* such as zone transfers that need to prevent the zone
* object from being freed but not from shutting down.
*
* Require:
* 'zone' to be a valid initalised zone.
* 'target' to be non NULL and '*target' to be NULL.
*/
void
dns_zone_idetach(dns_zone_t **zonep);
/*
* Detach from a zone decrementing its internal reference count.
* If there are no more internal or external references to the
* zone, it will be freed.
*
* Require:
* 'zonep' to point to a valid initalised zone.
*/
void
dns_zone_setflag(dns_zone_t *zone, unsigned int flags, isc_boolean_t value);
/*
......@@ -811,6 +787,12 @@ dns_zone_getmctx(dns_zone_t *zone);
dns_zonemgr_t *
dns_zone_getmgr(dns_zone_t *zone);
void
dns_zone_shutdown(dns_zone_t *zone);
/*
* Initiate shutdown for a zone.
*/
isc_result_t
dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
......@@ -845,15 +827,16 @@ dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr);
void
dns_zonemgr_shutdown(dns_zonemgr_t *zmgr);
/*
* Shut down and detach the task of the zone manager.
* Shut down the zone manager.
*/
void
dns_zonemgr_destroy(dns_zonemgr_t **zmgrp);
dns_zonemgr_detach(dns_zonemgr_t **zmgrp);
/*
* Destroy a zone manager.
* Detach from a zone manager.
*
* Requires:
*
* '*zmgrp' is a valid, non-NULL zone manager pointer.
* Ensures:
* '*zmgrp' is NULL.
......
......@@ -134,6 +134,15 @@ dns_zt_print(dns_zt_t *zt);
* 'zt' to be valid.
*/
void
dns_zt_shutdown(dns_zt_t *zt);
/*
* Shut down all zones in the table.
*
* Requires
* 'zt' to be valid.
*/
isc_result_t
dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
isc_result_t (*action)(dns_zone_t *, void *), void *uap);
......
......@@ -121,6 +121,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->frozen = ISC_FALSE;
view->task = NULL;
view->references = 1;
view->weakrefs = 0;
view->attributes = (DNS_VIEWATTR_RESSHUTDOWN|DNS_VIEWATTR_ADBSHUTDOWN|
DNS_VIEWATTR_REQSHUTDOWN);
view->statickeys = NULL;
......@@ -188,31 +189,11 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
return (result);
}
void
dns_view_attach(dns_view_t *source, dns_view_t **targetp) {
/*
* Attach '*targetp' to 'source'.
*/
REQUIRE(DNS_VIEW_VALID(source));
REQUIRE(targetp != NULL && *targetp == NULL);
LOCK(&source->lock);
INSIST(source->references > 0);
source->references++;
INSIST(source->references != 0);
UNLOCK(&source->lock);
*targetp = source;
}
static inline void
destroy(dns_view_t *view) {
REQUIRE(!ISC_LINK_LINKED(view, link));
REQUIRE(view->references == 0);
REQUIRE(view->weakrefs == 0);
REQUIRE(RESSHUTDOWN(view));
REQUIRE(ADBSHUTDOWN(view));
REQUIRE(REQSHUTDOWN(view));
......@@ -257,22 +238,35 @@ all_done(dns_view_t *view) {
* Caller must be holding the view lock.
*/
if (view->references == 0 && RESSHUTDOWN(view) &&
ADBSHUTDOWN(view) && REQSHUTDOWN(view))
if (view->references == 0 && view->weakrefs == 0 &&
RESSHUTDOWN(view) && ADBSHUTDOWN(view) && REQSHUTDOWN(view))
return (ISC_TRUE);
return (ISC_FALSE);
}
void
dns_view_attach(dns_view_t *source, dns_view_t **targetp) {
REQUIRE(DNS_VIEW_VALID(source));
REQUIRE(targetp != NULL && *targetp == NULL);
LOCK(&source->lock);
INSIST(source->references > 0);
source->references++;
INSIST(source->references != 0);
UNLOCK(&source->lock);
*targetp = source;
}
void
dns_view_detach(dns_view_t **viewp) {
dns_view_t *view;
isc_boolean_t done = ISC_FALSE;
/*
* Detach '*viewp' from its view.
*/
REQUIRE(viewp != NULL);
view = *viewp;
REQUIRE(DNS_VIEW_VALID(view));
......@@ -288,6 +282,7 @@ dns_view_detach(dns_view_t **viewp) {
dns_adb_shutdown(view->adb);
if (!REQSHUTDOWN(view))
dns_requestmgr_shutdown(view->requestmgr);
dns_zt_shutdown(view->zonetable);
done = all_done(view);
}
UNLOCK(&view->lock);
......@@ -298,6 +293,42 @@ dns_view_detach(dns_view_t **viewp) {
destroy(view);
}
void
dns_view_weakattach(dns_view_t *source, dns_view_t **targetp) {
REQUIRE(DNS_VIEW_VALID(source));
REQUIRE(targetp != NULL && *targetp == NULL);
LOCK(&source->lock);
source->weakrefs++;
UNLOCK(&source->lock);
*targetp = source;
}
void
dns_view_weakdetach(dns_view_t **viewp) {
dns_view_t *view;
isc_boolean_t done = ISC_FALSE;
REQUIRE(viewp != NULL);
view = *viewp;
REQUIRE(DNS_VIEW_VALID(view));
LOCK(&view->lock);
INSIST(view->weakrefs > 0);
view->weakrefs--;
done = all_done(view);
UNLOCK(&view->lock);
*viewp = NULL;
if (done)
destroy(view);
}
static void
resolver_shutdown(isc_task_t *task, isc_event_t *event) {
dns_view_t *view = event->ev_arg;
......
......@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: xfrin.c,v 1.69 2000/05/14 20:01:27 gson Exp $ */
/* $Id: xfrin.c,v 1.70 2000/05/17 19:45:29 gson Exp $ */
#include <config.h>
......@@ -605,7 +605,7 @@ xfrin_create(isc_mem_t *mctx,
xfr->mctx = mctx;
xfr->refcount = 0;
xfr->zone = NULL;
dns_zone_iattach(zone, &xfr->zone);
dns_zone_attach(zone, &xfr->zone);
xfr->task = NULL;
isc_task_attach(task, &xfr->task);
xfr->timer = NULL;
......@@ -1180,7 +1180,7 @@ maybe_free(dns_xfrin_ctx_t *xfr) {
dns_db_detach(&xfr->db);
if (xfr->zone != NULL)
dns_zone_idetach(&xfr->zone);
dns_zone_detach(&xfr->zone);
isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
}
......
......@@ -15,7 +15,7 @@
* SOFTWARE.
*/
/* $Id: zone.c,v 1.115 2000/05/14 23:23:37 gson Exp $ */
/* $Id: zone.c,v 1.116 2000/05/17 19:45:31 gson Exp $ */
#include <config.h>
......@@ -93,8 +93,7 @@ struct dns_zone {
dns_zonemgr_t *zmgr;
ISC_LINK(dns_zone_t) link; /* Used by zmgr. */
isc_timer_t *timer;
unsigned int erefs;
unsigned int irefs;
unsigned int refs;
dns_name_t origin;
char *dbname;
char *journal;
......@@ -178,6 +177,7 @@ struct dns_zone {
struct dns_zonemgr {
isc_mem_t * mctx;
int refs;
isc_taskmgr_t * taskmgr;
isc_timermgr_t * timermgr;
isc_socketmgr_t * socketmgr;
......@@ -223,7 +223,6 @@ static void zone_expire(dns_zone_t *zone);
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 releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone);
static void xfrdone(dns_zone_t *zone, isc_result_t result);
static void zone_shutdown(isc_task_t *, isc_event_t *);
......@@ -247,15 +246,18 @@ static isc_result_t zone_dump(dns_zone_t *);
static void got_transfer_quota(isc_task_t *task, isc_event_t *event);
static isc_result_t zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone);
static void zmgr_resume_xfrs(dns_zonemgr_t *zmgr);
static void zonemgr_free(dns_zonemgr_t *zmgr);
#define PRINT_ZONE_REF(zone) \
do { \
char *s = NULL; \
isc_result_t r; \
r = dns_zone_tostr(zone, zone->mctx, &s); \
if (r == ISC_R_SUCCESS) { \
printf("%p: %s: erefs = %d\n", zone, s, \
zone->erefs); \
printf("%p: %s: refs = %d\n", zone, s, \
zone->refs); \
isc_mem_free(zone->mctx, s); \
} \
} while (0)
......@@ -302,8 +304,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
zone->db = NULL;
zone->zmgr = NULL;
ISC_LINK_INIT(zone, link);
zone->erefs = 1; /* Implicit attach. */
zone->irefs = 0;
zone->refs = 1; /* Implicit attach. */
dns_name_init(&zone->origin, NULL);
zone->dbname = NULL;
zone->journalsize = -1;
......@@ -364,7 +365,7 @@ zone_free(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
LOCK(&zone->lock);
REQUIRE(zone->erefs == 0);
REQUIRE(zone->refs == 0);
zone->flags |= DNS_ZONE_F_EXITING;
UNLOCK(&zone->lock);
......@@ -379,13 +380,14 @@ zone_free(dns_zone_t *zone) {
dns_request_cancel(zone->request); /* XXXMPA */
dns_request_destroy(&zone->request); /* XXXMPA */
}
INSIST(zone->statelist == NULL);
if (zone->task != NULL)
isc_task_detach(&zone->task);
if (zone->zmgr)
dns_zonemgr_releasezone(zone->zmgr, zone);
INSIST(zone->statelist == NULL);
/* Unmanaged objects */
if (zone->dbname != NULL)
isc_mem_free(zone->mctx, zone->dbname);
......@@ -484,7 +486,7 @@ dns_zone_setdbtype(dns_zone_t *zone, char *db_type) {
void
dns_zone_setview(dns_zone_t *zone, dns_view_t *view) {
zone->view = view;
dns_view_weakattach(view, &zone->view);
}
......@@ -856,93 +858,74 @@ dns_zone_load(dns_zone_t *zone) {
static void
exit_check(dns_zone_t *zone) {
if (zone->irefs == 0 && DNS_ZONE_FLAG(zone, DNS_ZONE_F_EXITING))
if (zone->refs == 0)
zone_free(zone);
}
void
dns_zone_attach(dns_zone_t *source, dns_zone_t **target) {
REQUIRE(DNS_ZONE_VALID(source));
REQUIRE(target != NULL && *target == NULL);
LOCK(&source->lock);
REQUIRE(source->erefs > 0);
source->erefs++;
INSIST(source->erefs != 0xffffffffU);
UNLOCK(&source->lock);
*target = source;
}
void
dns_zone_detach(dns_zone_t **zonep) {
dns_zone_t *zone;
isc_boolean_t free_now = ISC_FALSE;
REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
zone = *zonep;
dns_zone_shutdown(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
LOCK(&zone->lock);
REQUIRE(zone->erefs > 0);
zone->erefs--;
if (zone->erefs == 0) {
if (zone->task != NULL) {
/*
* This zone is being managed. Post
* its control event and let it clean
* up synchronously in the context of
* its task.
*/
isc_event_t *ev = &zone->ctlevent;
isc_task_send(zone->task, &ev);
} else {
/*
* This zone is not being managed; it has
* no task and can have no outstanding
* events. Free it immediately.
*/
free_now = ISC_TRUE;
}
REQUIRE(zone->refs > 0);
if (zone->task != NULL) {
/*
* This zone is being managed. Post
* its control event and let it clean
* up synchronously in the context of
* its task.
*/
isc_event_t *ev = &zone->ctlevent;
isc_task_send(zone->task, &ev);
} else {
/*
* Unmanaged zones should not have non-null views;
* we have no way of detaching from the view here
* without causing deadlock because this code is called
* with the view already locked.
*/
INSIST(zone->view != NULL);
}
UNLOCK(&zone->lock);
if (free_now)
zone_free(zone);
*zonep = NULL;
}
static void
zone_iattach(dns_zone_t *source, dns_zone_t **target) {
zone_attach(dns_zone_t *source, dns_zone_t **target) {
REQUIRE(DNS_ZONE_VALID(source));
REQUIRE(target != NULL && *target == NULL);
source->irefs++;
INSIST(source->irefs != 0xffffffffU);
source->refs++;
INSIST(source->refs != 0xffffffffU);
*target = source;
}
void
dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) {
dns_zone_attach(dns_zone_t *source, dns_zone_t **target) {
REQUIRE(DNS_ZONE_VALID(source));
LOCK(&source->lock);
zone_iattach(source, target);
zone_attach(source, target);
UNLOCK(&source->lock);
}
static void
zone_idetach(dns_zone_t **zonep) {
zone_detach(dns_zone_t **zonep) {
dns_zone_t *zone;
REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
zone = *zonep;
REQUIRE(zone->irefs > 0);
zone->irefs--;
REQUIRE(zone->refs > 0);
zone->refs--;
*zonep = NULL;
}
void
dns_zone_idetach(dns_zone_t **zonep) {
dns_zone_detach(dns_zone_t **zonep) {
dns_zone_t *zone;
REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
zone = *zonep;
LOCK(&zone->lock);
zone_idetach(zonep);
zone_detach(zonep);
UNLOCK(&zone->lock);
exit_check(zone);
}
......@@ -1483,7 +1466,8 @@ notify_destroy(notify_t *notify) {
if (notify->zone != NULL) {
if (ISC_LINK_LINKED(notify, link))
ISC_LIST_UNLINK(notify->zone->notifies, notify, link);
zone_idetach(&notify->zone);
zone_detach(&notify->zone);
/* XXXAG exit_check */
}
if (notify->find != NULL)
dns_adb_destroyfind(&notify->find);
......@@ -1530,7 +1514,7 @@ process_adb_event(isc_task_t *task, isc_event_t *ev) {
REQUIRE(DNS_NOTIFY_VALID(notify));
result = ev->ev_type;
isc_event_free(&ev);
dns_zone_iattach(notify->zone, &zone);
dns_zone_attach(notify->zone, &zone);
if (result == DNS_EVENT_ADBNOMOREADDRESSES) {
LOCK(&notify->zone->lock);
notify_send(notify);
......@@ -1546,7 +1530,7 @@ process_adb_event(isc_task_t *task, isc_event_t *ev) {
notify_destroy(notify);
UNLOCK(&zone->lock);
detach:
dns_zone_idetach(&zone);
dns_zone_detach(&zone);
}
static void
......@@ -1559,7 +1543,7 @@ notify_find_address(notify_t *notify) {
options = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_INET |
DNS_ADBFIND_INET6 | DNS_ADBFIND_RETURNLAME;
dns_zone_iattach(notify->zone, &zone);
dns_zone_attach(notify->zone, &zone);
result = dns_adb_createfind(zone->view->adb,
zone->task,
process_adb_event, notify,
......@@ -1571,13 +1555,13 @@ notify_find_address(notify_t *notify) {
LOCK(&zone->lock);
notify_destroy(notify);
UNLOCK(&zone->lock);
dns_zone_idetach(&zone);
dns_zone_detach(&zone);
return;
}
/* More addresses pending? */
if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
dns_zone_idetach(&zone);
dns_zone_detach(&zone);
return;
}
......@@ -1585,7 +1569,7 @@ notify_find_address(notify_t *notify) {
LOCK(&zone->lock);
notify_send(notify);
UNLOCK(&zone->lock);
dns_zone_idetach(&zone);
dns_zone_detach(&zone);
}
......@@ -1621,7 +1605,7 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
UNUSED(task);