diff --git a/knot-resolver.files b/knot-resolver.files index d7b8b74a17b60c52b3e17a70080c5b20ffcc6bc1..d2d7480c47d5396db665afc8cf9fec33c620edaa 100644 --- a/knot-resolver.files +++ b/knot-resolver.files @@ -21,3 +21,4 @@ lib/rplan.h lib/rplan.c lib/layer/stats.h lib/layer/stats.c +lib/defines.h diff --git a/lib/context.c b/lib/context.c index 7e371cddde2344847a29a9587c857d3cf1efb5ba..b07849e71c5f8534c9c827bef3315ca16776f5f5 100644 --- a/lib/context.c +++ b/lib/context.c @@ -54,7 +54,6 @@ int kr_result_init(struct kr_context *ctx, struct kr_result *result) knot_wire_set_qr(ans->wire); result->ans = ans; - gettimeofday(&result->t_start, NULL); return 0; } diff --git a/lib/context.h b/lib/context.h index c07104814c2b012b9015615b7c164de1aa416b92..ee1c8ce82ab2f798bcea5fd17c6a2d3167447181 100644 --- a/lib/context.h +++ b/lib/context.h @@ -27,6 +27,7 @@ struct kr_result { knot_pkt_t *ans; unsigned flags; struct timeval t_start, t_end; + unsigned total_rtt; unsigned nr_queries; }; diff --git a/lib/defines.h b/lib/defines.h new file mode 100644 index 0000000000000000000000000000000000000000..e109834d1deab30665385bcd1e66927b27b3a3fd --- /dev/null +++ b/lib/defines.h @@ -0,0 +1,21 @@ +/* Copyright 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +/* + * Connection limits. + */ +#define KR_CONN_RTT_MAX 10000 diff --git a/lib/delegpt.c b/lib/delegpt.c index 58165e9895bf2d409ea50d2bc5de83cf18fd4168..54d3ec6a54a1cff29eb26af6de5f03c98f9213cb 100644 --- a/lib/delegpt.c +++ b/lib/delegpt.c @@ -1,4 +1,5 @@ #include "lib/delegpt.h" +#include "lib/defines.h" #include <common/mempool.h> static void delegpt_free(struct kr_ns *dp, mm_ctx_t *mm) @@ -104,6 +105,22 @@ struct kr_ns *kr_ns_find(list_t *list, const knot_dname_t *name) return NULL; } +void kr_ns_invalidate(struct kr_ns *ns) +{ + /* Slow start. */ + ns->flags = DP_LAME; + ns->stat.M = KR_CONN_RTT_MAX; + ns->stat.S = 0; + ns->stat.n = 1; + + /* Move to the end of the preference list. */ + node_t *next = ns->node.next; + if (next->next) { + rem_node(&ns->node); + insert_node(&ns->node, next); + } +} + void kr_ns_remove(struct kr_ns *ns, mm_ctx_t *mm) { rem_node((node_t *)ns); diff --git a/lib/delegpt.h b/lib/delegpt.h index 6a5b6ad237aea284042b4321c13400d5fd6ce5f1..a2841515f7acbc74ff534e368dcc0ee2a68abd77 100644 --- a/lib/delegpt.h +++ b/lib/delegpt.h @@ -35,7 +35,10 @@ struct kr_ns { knot_dname_t *name; struct sockaddr_storage addr; unsigned valid_until; - unsigned mean_rtt; + struct { + double M, S; /* Mean, Variance S/n */ + unsigned n; + } stat; unsigned flags; }; @@ -56,4 +59,6 @@ list_t *kr_delegmap_find(struct kr_delegmap *map, const knot_dname_t *name); struct kr_ns *kr_ns_get(list_t *list, const knot_dname_t *name, mm_ctx_t *mm); struct kr_ns *kr_ns_find(list_t *list, const knot_dname_t *name); +void kr_ns_invalidate(struct kr_ns *ns); void kr_ns_remove(struct kr_ns *ns, mm_ctx_t *mm); + diff --git a/lib/layer/stats.c b/lib/layer/stats.c index f0f2fcd682a09675e4a76ceed2795182ec0fd2e0..4b66c0f21a66c964cecb4018b677933bfc72a93b 100644 --- a/lib/layer/stats.c +++ b/lib/layer/stats.c @@ -20,6 +20,44 @@ limitations under the License. #define DEBUG_MSG(fmt, ...) fprintf(stderr, "[stats] " fmt, ## __VA_ARGS__) +static void update_ns_preference(struct kr_ns *ns, struct kr_ns *next) +{ + assert(ns); + assert(next); + + /* Push down if next has better score. */ + if (next->stat.M < ns->stat.M) { + rem_node(&ns->node); + insert_node(&ns->node, &next->node); + } +} + +static void update_ns_preference_list(struct kr_ns *cur) +{ + assert(cur); + struct kr_ns *next = (struct kr_ns *)cur->node.next; + + /* O(n), walk the list (shouldn't be too large). */ + /* TODO: cut on first swap? random swaps? */ + while (next->node.next != NULL) { + update_ns_preference(cur, next); + cur = next; + next = (struct kr_ns *)cur->node.next; + } +} + +static void update_stats(struct kr_ns *ns, double rtt) +{ + /* Knuth, TAOCP, p.232 (Welford running variance/mean). */ + double d_mean = (rtt - ns->stat.M); + ns->stat.n += 1; + ns->stat.M += d_mean / ns->stat.n; + ns->stat.S += d_mean * (rtt - ns->stat.M); + + /* Update NS position in preference list. */ + update_ns_preference_list(ns); +} + static int begin(knot_layer_t *ctx, void *param) { ctx->data = param; @@ -38,7 +76,7 @@ static int finish(knot_layer_t *ctx) DEBUG_MSG("rcode: %d (%u RRs)\n", knot_wire_get_rcode(result->ans->wire), result->ans->rrset_count); DEBUG_MSG("queries: %u\n", result->nr_queries); - DEBUG_MSG("total time: %.02f msecs\n", time_diff(&result->t_start, &result->t_end)); + DEBUG_MSG("total time: %u msecs\n", result->total_rtt); #endif return ctx->state; @@ -51,6 +89,9 @@ static int query(knot_layer_t *ctx, knot_pkt_t *pkt) result->nr_queries += 1; + /* Store stats. */ + gettimeofday(&result->t_start, NULL); + return ctx->state; } @@ -60,19 +101,27 @@ static int answer(knot_layer_t *ctx, knot_pkt_t *pkt) struct kr_layer_param *param = ctx->data; struct kr_context* resolve = param->ctx; struct kr_result *result = param->result; + struct kr_ns *ns = resolve->current_ns; /* Store stats. */ gettimeofday(&result->t_end, NULL); + /* Update NS statistics. */ + double rtt = time_diff(&result->t_start, &result->t_end); + if (rtt > 0.0) { + update_stats(ns, rtt); + result->total_rtt += rtt; + } + #ifndef NDEBUG - char *ns_name = knot_dname_to_str(resolve->current_ns->name); + char *ns_name = knot_dname_to_str(ns->name); char pad[16]; memset(pad, '-', sizeof(pad)); pad[MIN(sizeof(pad) - 1, list_size(&resolve->rplan.q) * 2)] = '\0'; - DEBUG_MSG("#%s %s ... RC=%d, AA=%d, cumulative time: %.02f msecs\n", + DEBUG_MSG("#%s %s ... RC=%d, AA=%d, RTT: %.02f msecs\n", pad, ns_name, knot_wire_get_rcode(pkt->wire), knot_wire_get_aa(pkt->wire) != 0, - time_diff(&result->t_start, &result->t_end)); + rtt); free(ns_name); #endif diff --git a/lib/resolve.c b/lib/resolve.c index 409b16837f16a8baddf477c6e65becab4b73e44e..9fc4362e58cd415cef9551f84fea91dfd8bfa346 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -4,6 +4,7 @@ #include <libknot/processing/requestor.h> #include <libknot/descriptor.h> #include "lib/resolve.h" +#include "lib/defines.h" #include "lib/layer/iterate.h" #include "lib/layer/static.h" #include "lib/layer/stats.h" @@ -29,7 +30,7 @@ static int resolve_ns(struct kr_context *resolve, struct kr_ns *ns) static void iterate(struct knot_requestor *requestor, struct kr_context* ctx) { - struct timeval timeout = { 5, 0 }; + struct timeval timeout = { KR_CONN_RTT_MAX / 1000, 0 }; const struct kr_query *next = kr_rplan_next(&ctx->rplan); assert(next); @@ -66,7 +67,8 @@ static void iterate(struct knot_requestor *requestor, struct kr_context* ctx) knot_requestor_enqueue(requestor, tx); int ret = knot_requestor_exec(requestor, &timeout); if (ret != 0) { - kr_ns_remove(ns, ctx->dp_map.pool); + /* Resolution failed, invalidate current resolver. */ + kr_ns_invalidate(ns); } /* Pop resolved query. */