From ea17368c01cdfbd794b640796122c08d8df4127a Mon Sep 17 00:00:00 2001
From: Marek Vavrusa <marek.vavrusa@nic.cz>
Date: Tue, 2 Apr 2013 12:21:28 +0200
Subject: [PATCH] Configurable number of parallel xfers (system.transfers).

Updated documentation, cleanup.
---
 doc/reference.texi            | 12 ++++++++-
 samples/knot.full.conf        |  6 +++++
 src/knot/conf/cf-lex.l        |  1 +
 src/knot/conf/cf-parse.y      |  2 ++
 src/knot/conf/conf.c          |  4 +++
 src/knot/conf/conf.h          |  2 ++
 src/knot/ctl/remote.c         |  8 +-----
 src/knot/server/xfr-handler.c | 46 +++++++++++------------------------
 src/knot/server/xfr-handler.h |  7 ++++++
 src/knot/server/zones.c       |  4 ++-
 src/knot/server/zones.h       |  1 +
 11 files changed, 52 insertions(+), 41 deletions(-)

diff --git a/doc/reference.texi b/doc/reference.texi
index bb2bf39184..40488bcd35 100644
--- a/doc/reference.texi
+++ b/doc/reference.texi
@@ -42,8 +42,9 @@ else.
   [ @code{max-conn-idle} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
   [ @code{max-conn-hs} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
   [ @code{max-conn-reply} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
+  [ @code{transfers} @kbd{integer}@code{;} ]
   [ @code{rate-limit} @kbd{integer}@code{;} ]
-  [ @code{rate-limit-size} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
+  [ @code{rate-limit-size} @kbd{integer}@code{;} ]
   [ @code{rate-limit-slip} @kbd{integer}@code{;} ]
 @code{@}}
 @end example
@@ -62,6 +63,7 @@ else.
 * max-conn-idle::               
 * max-conn-hs::                 
 * max-conn-reply::              
+* transfers::
 * rate-limit::
 * rate-limit-size::
 * rate-limit-slip::
@@ -184,6 +186,14 @@ that already made at least 1 meaningful query.
 
 Maximum time to wait for a reply to an issued SOA query.
 
+@node transfers
+@subsubsection transfers
+@vindex transfers
+
+Maximum parallel transfers, including pending SOA queries.
+Lowest possible number is the number of CPUs.
+Default is 10.
+
 @node rate-limit
 @subsubsection rate-limit
 @vindex rate-limit
diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index 8c02d3c11f..aabe334c38 100644
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -61,6 +61,12 @@ system {
   # Default: 10s
   max-conn-reply 10s;
 
+  # Number of parallel transfers
+  # This number also includes pending SOA queries
+  # Minimal value is number of CPUs
+  # Default: 10
+  transfers 10; 
+
   # Rate limit
   # in queries / second
   # Default: off (=0)
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index 5a539179ad..0b0ddf88b1 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -98,6 +98,7 @@ max-conn-reply  { lval.t = yytext; return MAX_CONN_REPLY; }
 rate-limit      { lval.t = yytext; return RATE_LIMIT; }
 rate-limit-size { lval.t = yytext; return RATE_LIMIT_SIZE; }
 rate-limit-slip { lval.t = yytext; return RATE_LIMIT_SLIP; }
+transfers       { lval.t = yytext; return TRANSFERS; }
 
 interfaces      { lval.t = yytext; return INTERFACES; }
 address         { lval.t = yytext; return ADDRESS; }
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index ff0097b21c..f6aa2b471f 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -299,6 +299,7 @@ static int conf_mask(void* scanner, int nval, int prefixlen) {
 %token <tok> RATE_LIMIT
 %token <tok> RATE_LIMIT_SIZE
 %token <tok> RATE_LIMIT_SLIP
+%token <tok> TRANSFERS
 
 %token <tok> INTERFACES ADDRESS PORT
 %token <tok> IPA
@@ -444,6 +445,7 @@ system:
  | system RATE_LIMIT_SIZE SIZE ';' { new_config->rrl_size = $3.l; }
  | system RATE_LIMIT_SIZE NUM ';' { new_config->rrl_size = $3.i; }
  | system RATE_LIMIT_SLIP NUM ';' { new_config->rrl_slip = $3.i; }
+ | system TRANSFERS NUM ';' { new_config->xfers = $3.i; }
  ;
 
 keys:
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 31b380cf2f..f5104de4ee 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -178,6 +178,9 @@ static int conf_process(conf_t *conf)
 	if (conf->rrl_size == 0) {
 		conf->rrl_size = CONFIG_RRL_SIZE;
 	}
+	
+	/* Default parallel transfers. */
+	if (conf->xfers <= 0) conf->xfers = CONFIG_XFERS;
 
 	// Postprocess zones
 	int ret = KNOT_EOK;
@@ -496,6 +499,7 @@ conf_t *conf_new(const char* path)
 	c->ixfr_fslimit = -1;
 	c->uid = -1;
 	c->gid = -1;
+	c->xfers = -1;
 	c->build_diffs = 0; /* Disable by default. */
 	
 	/* ACLs. */
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index 2c87b1da46..2f9f036da0 100644
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -52,6 +52,7 @@
 #define CONFIG_IDLE_WD  60 /*!< [secs] of allowed inactivity between requests */
 #define CONFIG_RRL_SLIP 2 /*!< Default slip value. */
 #define CONFIG_RRL_SIZE 393241 /*!< Htable default size. */
+#define CONFIG_XFERS 10
 
 /*!
  * \brief Configuration for the interface
@@ -185,6 +186,7 @@ typedef struct conf_t {
 	int    rrl;      /*!< Rate limit (in responses per second). */
 	size_t rrl_size; /*!< Rate limit htable size. */
 	int    rrl_slip;  /*!< Rate limit SLIP. */
+	int    xfers;     /*!< Number of parallel transfers. */
 
 	/*
 	 * Log
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index 8bb527ced6..a9c9fa5679 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -229,14 +229,8 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a)
 		
 		/* Evaluate zone state. */
 		char *when = NULL;
-//		int locked = pthread_mutex_trylock(&zd->xfr_in.lock);
-//		if (locked == 0) pthread_mutex_unlock(&zd->xfr_in.lock);
-		int locked = 0;
-		fprintf(stderr, "TODO: use something else\n");
-		if (locked != 0) {
+		if (zd->xfr_in.state == XFR_PENDING) {
 			when = strdup("pending");
-//		} else if (zd->xfr_in.scheduled) {
-//			when = strdup("scheduled");
 		} else if (zd->xfr_in.timer) {
 			struct timeval now, dif;
 			gettimeofday(&now, 0);
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 686877648e..54e6e558ae 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -45,7 +45,6 @@
 
 /* Constants */
 #define XFR_CHUNKLEN 3 /*! Number of requests assigned in a single pass. */
-#define XFR_QLEN 10 /*! Maximum pending transfers. \todo Configurable? */
 #define XFR_SWEEP_INTERVAL 2 /*! [seconds] between sweeps. */
 #define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */
 #define XFR_MSG_DLTTR 9 /*! Index of letter differentiating IXFR/AXFR in log msg. */
@@ -473,12 +472,12 @@ static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, siz
 	switch(rq->type) {
 	case XFR_TYPE_AIN:
 	case XFR_TYPE_IIN:
+		zd->xfr_in.state = XFR_PENDING;
 		rq->lookup_tree = hattrie_create();
 		if (ret != KNOT_EOK && ret != KNOT_EACCES) {
 			if (zd != NULL && !knot_zone_contents(rq->zone)) {
 				/* Reschedule request delay. */
-				int tmr_s = AXFR_BOOTSTRAP_RETRY;
-				tmr_s += (int)((tmr_s) * tls_rand());
+				int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand();
 				event_t *ev = zd->xfr_in.timer;
 				if (ev) {
 					evsched_cancel(ev->parent, ev);
@@ -705,7 +704,6 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 			ret = xfr_task_finalize(w, rq);
 			
 			/* AXFR bootstrap timeout. */
-			/*! \todo This is probably unneccesary. */
 			rcu_read_lock();
 			zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
 			if (ret != KNOT_EOK && !knot_zone_contents(rq->zone)) {
@@ -941,9 +939,9 @@ int xfr_worker(dthread_t *thread)
 	time_now(&next_sweep);
 	next_sweep.tv_sec += XFR_SWEEP_INTERVAL;
 
-	unsigned thread_capacity = XFR_QLEN / w->master->unit->size;
+	unsigned thread_capacity = conf()->xfers / w->master->unit->size;
 	w->pool.fds = fdset_new();
-	w->pool.t = ahtable_create_n(XFR_QLEN);
+	w->pool.t = ahtable_create_n(conf()->xfers);
 	w->pending = 0;
 
 	/* Accept requests. */
@@ -952,7 +950,7 @@ int xfr_worker(dthread_t *thread)
 	for (;;) {
 		
 		/* Populate pool with new requests. */
-		if (w->pending < thread_capacity) {
+		if (w->pending <= thread_capacity) {
 			pthread_mutex_lock(&xfr->mx);
 			unsigned was_pending = w->pending;
 			while (!EMPTY_LIST(xfr->queue)) { /* Take first request. */
@@ -1142,12 +1140,8 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 	
 	rcu_read_lock();
 	int ret = knot_ns_init_xfr(ns, rq);
-
-	int xfr_failed = (ret != KNOT_EOK);
-	const char *errstr = knot_strerror(ret);
 	
-	// use the QNAME as the zone name to get names also for
-	// zones that are not in the server
+	/* Use the QNAME as the zone name. */
 	const knot_dname_t *qname = knot_packet_qname(rq->query);
 	if (qname != NULL) {
 		rq->zname = knot_dname_to_str(qname);
@@ -1156,20 +1150,15 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 	}
 
 	/* Check requested zone. */
-	if (!xfr_failed) {
-		int zcheck_ret = zones_xfr_check_zone(rq, &rq->rcode);
-		xfr_failed = (zcheck_ret != KNOT_EOK);
-		errstr = knot_strerror(zcheck_ret);
+	if (ret == KNOT_EOK) {
+		ret = zones_xfr_check_zone(rq, &rq->rcode);
 	}
 
 	/* Check TSIG. */
 	char *keytag = NULL;
-	if (!xfr_failed && rq->tsig_key != NULL) {
+	if (ret == KNOT_EOK && rq->tsig_key != NULL) {
 		ret = xfr_check_tsig(rq, &rq->rcode, &keytag);
-		xfr_failed = (ret != KNOT_EOK);
-		errstr = knot_strerror(ret);
 	}
-	
 	if (xfr_task_setmsg(rq, keytag) != KNOT_EOK) {
 		rq->msg = strdup("XFR:");
 	}
@@ -1207,7 +1196,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 	}
 	
 	/* Finally, answer AXFR/IXFR. */
-	if (!xfr_failed) {
+	if (ret == KNOT_EOK) {
 		switch(rq->type) {
 		case XFR_TYPE_AOUT:
 			ret = xfr_answer_axfr(ns, rq);
@@ -1219,19 +1208,16 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 			ret = KNOT_ENOTSUP;
 			break;
 		}
-		
-		xfr_failed = (ret != KNOT_EOK);
-		errstr = knot_strerror(ret);
 	} else {
+		/*! \todo Sign with TSIG for some errors. */
 		knot_ns_error_response_from_query(ns, rq->query,  rq->rcode,
 		                                  rq->wire, &rq->wire_size);
-		/*! \todo Sign with TSIG for some errors. */
 		ret = rq->send(rq->session, &rq->addr, rq->wire, rq->wire_size);
 	}
 	
 	/* Check results. */
-	if (xfr_failed) {
-		log_server_notice("%s %s\n", rq->msg, errstr);
+	if (ret != KNOT_EOK) {
+		log_server_notice("%s %s\n", rq->msg, knot_strerror(ret));
 	} else {
 		log_server_info("%s Finished.\n", rq->msg);
 	}
@@ -1244,11 +1230,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *rq)
 	/* Free request. */
 	xfr_task_free(rq);
 	rcu_read_unlock();
-	if (xfr_failed) {
-		return KNOT_ERROR;
-	}
-	
-	return KNOT_EOK;
+	return ret;
 }
 
 knot_ns_xfr_t *xfr_task_create(knot_zone_t *z, int type, int flags)
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
index 614281fe7c..e221834d50 100644
--- a/src/knot/server/xfr-handler.h
+++ b/src/knot/server/xfr-handler.h
@@ -36,6 +36,13 @@
 
 struct xfrhandler_t;
 
+/*! \brief Transfer state. */
+enum xfrstate_t {
+	XFR_IDLE = 0,
+	XFR_SCHED,
+	XFR_PENDING,
+};
+
 /*!
  * \brief XFR worker structure.
  */
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 45910abef4..1385698964 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -3227,6 +3227,7 @@ int zones_schedule_refresh(knot_zone_t *zone)
 
 	/* Check XFR/IN master server. */
 	rcu_read_lock();
+	zd->xfr_in.state = XFR_IDLE;
 	if (zd->xfr_in.has_master) {
 
 		/* Schedule REFRESH timer. */
@@ -3234,12 +3235,13 @@ int zones_schedule_refresh(knot_zone_t *zone)
 		if (knot_zone_contents(zone)) {
 			refresh_tmr = zones_jitter(zones_soa_refresh(zone));
 		} else {
-			refresh_tmr = AXFR_BOOTSTRAP_RETRY * tls_rand();
+			refresh_tmr = zd->xfr_in.bootstrap_retry;
 		}
 		zd->xfr_in.timer = evsched_schedule_cb(sch, zones_refresh_ev,
 		                                       zone, refresh_tmr);
 		dbg_zones("zone: REFRESH '%s' set to %u\n",
 		          zd->conf->name, refresh_tmr);
+		zd->xfr_in.state = XFR_SCHED;
 	}
 	rcu_read_unlock();
 
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index 2bc5d8b844..a0881e05b1 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -76,6 +76,7 @@ typedef struct zonedata_t
 		struct event_t *expire;   /*!< Timer for REFRESH. */
 		uint32_t bootstrap_retry; /*!< AXFR/IN bootstrap retry. */
 		int has_master;           /*!< True if it has master set. */
+		unsigned state;
 	} xfr_in;
 
 	/*! \brief Zone IXFR history. */
-- 
GitLab