From 49fc819889fb6151c8fa7ee2684dad50d6726838 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Vavru=C5=A1a?= <marek.vavrusa@nic.cz>
Date: Tue, 8 Apr 2014 14:46:54 +0200
Subject: [PATCH] ddns: excluded ddns/zone discard, release the zone after the
 ddns lock

---
 src/knot/nameserver/update.c | 30 ++++++++++++++++++------------
 src/knot/server/zone-load.c  |  2 --
 src/knot/zone/zonedb.c       |  6 +++++-
 3 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
index ac3b18c13..afa4e8199 100644
--- a/src/knot/nameserver/update.c
+++ b/src/knot/nameserver/update.c
@@ -142,6 +142,12 @@ int update_answer(knot_pkt_t *pkt, struct query_data *qdata)
 		return NS_PROC_FAIL;
 	}
 
+	/* Check if the zone is not discarded. */
+	if (zone->flags & ZONE_DISCARDED) {
+		pthread_mutex_unlock(&zone->ddns_lock);
+		return NS_PROC_FAIL;
+	}
+
 	struct timeval t_start = {0}, t_end = {0};
 	gettimeofday(&t_start, NULL);
 	UPDATE_LOG(LOG_INFO, "Started (serial %u).", knot_zone_serial(qdata->zone->contents));
@@ -149,10 +155,22 @@ int update_answer(knot_pkt_t *pkt, struct query_data *qdata)
 	/* Reserve space for TSIG. */
 	knot_pkt_reserve(pkt, tsig_wire_maxsize(qdata->sign.tsig_key));
 
+	/* Retain zone for the whole processing so it doesn't disappear
+	 * for example during reload.
+	 * @note This is going to be fixed when this is made a zone event. */
+	zone_retain(zone);
+
 	/* Process UPDATE. */
+	rcu_read_unlock();
 	int ret = update_process(pkt, qdata);
+	rcu_read_lock();
 
+	/* Since we unlocked RCU read lock, it is possible that the
+	 * zone was modified/removed in the background. Therefore,
+	 * we must NOT touch the zone after we release it here. */
 	pthread_mutex_unlock(&zone->ddns_lock);
+	zone_release(zone);
+	qdata->zone = NULL;
 
 	/* Evaluate */
 	switch(ret) {
@@ -335,7 +353,6 @@ static int zones_process_update_auth(struct query_data *qdata)
 	}
 
 	// Apply changeset to zone created by DDNS processing
-
 	if (zone_config->dnssec_enable) {
 		/*!
 		 * Check if the UPDATE changed DNSKEYs. If yes, resign the whole
@@ -436,10 +453,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 	}
 
 	// Switch zone contents.
-	zone_retain(zone);      /* Retain pointer for safe RCU unlock. */
-	rcu_read_unlock();      /* Unlock for switch. */
 	ret = xfrin_switch_zone(zone, new_contents, XFR_TYPE_UPDATE);
-	rcu_read_lock();        /* Relock */
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s: Failed to replace current zone (%s)\n",
 		               msg, knot_strerror(ret));
@@ -449,8 +463,6 @@ static int zones_process_update_auth(struct query_data *qdata)
 
 		/* Free changesets, but not the data. */
 		zones_free_merged_changesets(chgsets, sec_chs);
-		zone_release(zone);
-		qdata->zone = NULL;
 		return KNOT_ERROR;
 	}
 
@@ -479,12 +491,6 @@ static int zones_process_update_auth(struct query_data *qdata)
 		zones_schedule_zonefile_sync(zone, 0);
 	}
 
-	/* Since we unlocked RCU read lock, it is possible that the
-	 * zone was modified/removed in the background. Therefore,
-	 * we must NOT touch the zone after we release it here. */
-	zone_release(zone);
-	qdata->zone = NULL;
-
 	return ret;
 }
 
diff --git a/src/knot/server/zone-load.c b/src/knot/server/zone-load.c
index 919d20071..226df6502 100644
--- a/src/knot/server/zone-load.c
+++ b/src/knot/server/zone-load.c
@@ -519,8 +519,6 @@ static int remove_old_zonedb(const knot_zonedb_t *db_new, knot_zonedb_t *db_old)
 		knot_zonedb_iter_next(&it);
 	}
 
-	synchronize_rcu();
-
 	/* Delete all deprecated zones and delete the old database. */
 	knot_zonedb_deep_free(&db_old);
 
diff --git a/src/knot/zone/zonedb.c b/src/knot/zone/zonedb.c
index e152a6e0d..4915969bb 100644
--- a/src/knot/zone/zonedb.c
+++ b/src/knot/zone/zonedb.c
@@ -40,8 +40,12 @@
 /*! \brief Discard zone in zone database. */
 static void discard_zone(zone_t *zone)
 {
-	synchronize_rcu();
+	/* @note Exclude DDNS. */
+	pthread_mutex_lock(&zone->ddns_lock);
 	zone->flags |= ZONE_DISCARDED;
+	pthread_mutex_unlock(&zone->ddns_lock);
+	/* Wait for current operations. */
+	synchronize_rcu();
 	zone_release(zone);
 }
 
-- 
GitLab