diff --git a/Knot.files b/Knot.files index befe0ffcaf6f09e79ec1e8571dde467ac7476975..6a51d053339ee8d8bce1abbfe4fbee6486698af6 100644 --- a/Knot.files +++ b/Knot.files @@ -20,11 +20,13 @@ samples/Makefile.am src/Makefile.am src/common/acl.c src/common/acl.h +src/common/array-sort.h src/common/atomic.h src/common/base32hex.c src/common/base32hex.h src/common/base64.c src/common/base64.h +src/common/binsearch.h src/common/crc.h src/common/dSFMT-params.h src/common/dSFMT-params521.h @@ -284,3 +286,4 @@ tests/tap/float.h tests/tap/macros.h tests/wire.c tests/ztree.c +tests/zonedb.c diff --git a/src/Makefile.am b/src/Makefile.am index 972a94f3182378476675293c256f889e81fcd717..82fe55b0965b969b05388527f9a2cecc2ec6d39a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -177,7 +177,9 @@ libknots_la_SOURCES = \ common/slab/slab.c \ common/slab/slab.h \ common/slab/alloc-common.h \ + common/array-sort.h \ common/atomic.h \ + common/binsearch.h \ common/memdup.h \ common/mempattern.h \ common/mempattern.c \ diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index 42152989aaafc978d0cb01e0280755b775c7cff9..6b19d6d1f5d9e940a92cb36aa2dd046dbc836466 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -335,7 +335,6 @@ static int remote_c_zonestatus(server_t *s, remote_cmdargs_t* a) } rcu_read_unlock(); - free(zones); a->rlen = sizeof(a->resp) - 1 - rb; return ret; @@ -382,7 +381,6 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a) ret = remote_zone_flush(s, zones[i]); } rcu_read_unlock(); - free(zones); return ret; } diff --git a/src/knot/main.c b/src/knot/main.c index 835b826f95307a28c6c683181f39fee6d15473d6..9c593da32d2684803733b2a848b7308f002ebf7e 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -314,7 +314,7 @@ int main(int argc, char **argv) int res = 0; log_server_info("Starting server...\n"); if ((server_start(server)) == KNOT_EOK) { - size_t zcount = server->nameserver->zone_db->zone_count; + size_t zcount = server->nameserver->zone_db->count; if (!zcount) { log_server_warning("Server started, but no zones served.\n"); } diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 1e5f56eb68f3711123c4cf92a102edcc33a71d5a..a94df55670b1b0d03407e0288b3aae74c8e372e2 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -474,10 +474,6 @@ int server_refresh(server_t *server) knot_nameserver_t *ns = server->nameserver; evsched_t *sch = ((server_t *)knot_ns_get_data(ns))->sched; const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); - if (zones == NULL) { - rcu_read_unlock(); - return KNOT_ENOMEM; - } /* REFRESH zones. */ for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { @@ -496,7 +492,6 @@ int server_refresh(server_t *server) /* Unlock RCU. */ rcu_read_unlock(); - free(zones); return KNOT_EOK; } diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index a7395782c33f2ddd74a1cc285a46ab39e7baa546..c1eec6c738ce2d535a5fb7ee8ad26407414564ca 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -1556,80 +1556,58 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst, return ret; } -/*! \brief Structure for multithreaded zone loading. */ -struct zonewalk_t { +/*! \brief Context for threaded zone loader. */ +typedef struct { + const struct conf_t *config; knot_nameserver_t *ns; knot_zonedb_t *db_new; pthread_mutex_t lock; - int inserted; - unsigned qhead; - unsigned qtail; - conf_zone_t *q[]; - -}; +} zone_loader_ctx_t; /*! Thread entrypoint for loading zones. */ -static int zonewalker(dthread_t *thread) +static int zones_loader_thread(dthread_t *thread) { - if (thread == NULL) { + if (thread == NULL || thread->data == NULL) { return KNOT_ERROR; } - struct zonewalk_t *zw = (struct zonewalk_t *)thread->data; - if (zw == NULL) { - return KNOT_ERROR; - } - - unsigned i = 0; - int inserted = 0; - knot_zone_t **zones = NULL; - size_t allocd = 0; + int ret = KNOT_ERROR; + knot_zone_t *zone = NULL; + conf_zone_t *zone_config = NULL; + zone_loader_ctx_t *ctx = (zone_loader_ctx_t *)thread->data; for(;;) { - /* Fetch queue head. */ - pthread_mutex_lock(&zw->lock); - i = zw->qhead++; - pthread_mutex_unlock(&zw->lock); - if (i >= zw->qtail) { + /* Fetch zone configuration from the list. */ + pthread_mutex_lock(&ctx->lock); + if (EMPTY_LIST(ctx->config->zones)) { + pthread_mutex_unlock(&ctx->lock); break; } - if (mreserve((char **)&zones, sizeof(knot_zone_t*), - inserted + 1, 32, &allocd) < 0) { - dbg_zones("zones: failed to reserve space for " - "loading zones\n"); - continue; - } + /* Disconnect from the list and start processing. */ + zone_config = HEAD(ctx->config->zones); + rem_node(&zone_config->n); + pthread_mutex_unlock(&ctx->lock); + ret = zones_insert_zone(zone_config, &zone, ctx->ns); - int ret = zones_insert_zone(zw->q[i], zones + inserted, zw->ns); + /* Insert into database if properly loaded. */ + pthread_mutex_lock(&ctx->lock); if (ret == KNOT_EOK) { - ++inserted; - } - } - - /* Collect results. */ - pthread_mutex_lock(&zw->lock); - zw->inserted += inserted; - for (int i = 0; i < inserted; ++i) { - zonedata_t *zd = (zonedata_t *)knot_zone_data(zones[i]); - if (knot_zonedb_add_zone(zw->db_new, zones[i]) != KNOT_EOK) { - log_server_error("Failed to insert zone '%s' " - "into database.\n", zd->conf->name); - /* Not doing this here would result in memory errors. */ - rem_node(&zd->conf->n); - knot_zone_deep_free(zones + i); + if (knot_zonedb_add_zone(ctx->db_new, zone) != KNOT_EOK) { + log_server_error("Failed to insert zone '%s' " + "into database.\n", zone_config->name); + knot_zone_deep_free(&zone); + } } else { - /* Unlink zone config from conf(), - * transferring ownership to zonedata. */ - rem_node(&zd->conf->n); + /* Unable to load, discard configuration. */ + conf_free_zone(zone_config); } + pthread_mutex_unlock(&ctx->lock); } - pthread_mutex_unlock(&zw->lock); - free(zones); return KNOT_EOK; } -static int zonewalker_destruct(dthread_t *thread) +static int zones_loader_destruct(dthread_t *thread) { knot_dnssec_thread_cleanup(); return KNOT_EOK; @@ -1642,50 +1620,30 @@ static int zonewalker_destruct(dthread_t *thread) * new. New zones are loaded. * * \param ns Name server instance. - * \param zone_conf Zone configuration. - * \param db_new New zone database. + * \param conf Server configuration. * * \return Number of inserted zones. */ -static int zones_insert_zones(knot_nameserver_t *ns, - const list_t *zone_conf, - knot_zonedb_t *db_new) +static knot_zonedb_t *zones_load_zonedb(knot_nameserver_t *ns, const conf_t *conf) { - int ret = 0; - size_t zcount = 0; - conf_zone_t *z = NULL; - WALK_LIST(z, *zone_conf) { - ++zcount; - } - if (zcount == 0) - return 0; - - /* Initialize zonewalker. */ - size_t zwlen = sizeof(struct zonewalk_t) + zcount * sizeof(conf_zone_t*); - struct zonewalk_t *zw = malloc(zwlen); - if (zw == NULL) { - return KNOT_ENOMEM; - } - memset(zw, 0, zwlen); - zw->ns = ns; - zw->db_new = db_new; - zw->inserted = 0; - if (pthread_mutex_init(&zw->lock, NULL) < 0) { - free(zw); - return KNOT_ENOMEM; + /* Initialize threaded loader. */ + int ret = KNOT_EOK; + zone_loader_ctx_t ctx; + ctx.ns = ns; + ctx.config = conf; + ctx.db_new = knot_zonedb_new(conf->zones_count); + if (ctx.db_new == NULL) { + return NULL; } - unsigned i = 0; - WALK_LIST(z, *zone_conf) { - zw->q[i++] = z; + if (pthread_mutex_init(&ctx.lock, NULL) < 0) { + knot_zonedb_free(&ctx.db_new); + return NULL; } - zw->qhead = 0; - zw->qtail = zcount; /* Initialize threads. */ - size_t thrs = dt_optimal_size(); - if (thrs > zcount) thrs = zcount; - dt_unit_t *unit = dt_create_coherent(thrs, &zonewalker, - &zonewalker_destruct, zw); + size_t thread_count = MIN(conf->zones_count, dt_optimal_size()); + dt_unit_t *unit = NULL; + unit = dt_create_coherent(thread_count, &zones_loader_thread, &zones_loader_destruct, &ctx); if (unit != NULL) { /* Start loading. */ dt_start(unit); @@ -1693,14 +1651,13 @@ static int zones_insert_zones(knot_nameserver_t *ns, dt_delete(&unit); /* Collect counts. */ - ret = zw->inserted; + ret = knot_zonedb_zone_count(ctx.db_new); } else { ret = KNOT_ENOMEM; } - pthread_mutex_destroy(&zw->lock); - free(zw); - return ret; + pthread_mutex_destroy(&ctx.lock); + return ctx.db_new; } /*----------------------------------------------------------------------------*/ @@ -1719,34 +1676,23 @@ static int zones_insert_zones(knot_nameserver_t *ns, static int zones_remove_zones(const knot_zonedb_t *db_new, knot_zonedb_t *db_old) { - hattrie_iter_t *i = hattrie_iter_begin(db_new->zone_tree, 0); - while(!hattrie_iter_finished(i)) { + unsigned new_zone_count = db_new->count; + const knot_zone_t **new_zones = knot_zonedb_zones(db_new); + const knot_zone_t *old_zone = NULL; + for (unsigned i = 0; i < new_zone_count; ++i) { /* try to find the new zone in the old DB * if the pointers match, remove the zone from old DB */ - /*! \todo Find better way of removing zone with given pointer.*/ - knot_zone_t *new_zone = (knot_zone_t *)(*hattrie_iter_val(i)); - knot_zone_t *old_zone = knot_zonedb_find_zone( - db_old, knot_zone_name(new_zone)); - if (old_zone == new_zone) { -dbg_zones_exec( - char *name = knot_dname_to_str(knot_zone_name(old_zone)); - dbg_zones_verb("zones: zone pointers match, removing zone %s " - "from database.\n", name); - free(name); -); - + old_zone = knot_zonedb_find_zone(db_old, knot_zone_name(new_zones[i])); + if (old_zone == new_zones[i]) { /* Remove from zone db. */ knot_zone_t * rm = knot_zonedb_remove_zone(db_old, knot_zone_name(old_zone)); assert(rm == old_zone); } - hattrie_iter_next(i); } - hattrie_iter_free(i); - return KNOT_EOK; } @@ -2125,28 +2071,19 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns, } rcu_read_unlock(); - /* Create new zone DB */ - knot_zonedb_t *db_new = knot_zonedb_new(); - if (db_new == NULL) { - return KNOT_ERROR; - } - - log_server_info("Loading %d zones...\n", conf->zones_count); - /* Insert all required zones to the new zone DB. */ /*! \warning RCU must not be locked as some contents switching will be required. */ - int inserted = zones_insert_zones(ns, &conf->zones, db_new); - if (inserted < 0) { - log_server_warning("Failed to load zones - %s\n", - knot_strerror(inserted)); - inserted = 0; - } - log_server_info("Loaded %d out of %d zones.\n", inserted, - conf->zones_count); - - if (inserted != conf->zones_count) { - log_server_warning("Not all the zones were loaded.\n"); + knot_zonedb_t *db_new = zones_load_zonedb(ns, conf); + if (db_new == NULL) { + log_server_warning("Failed to load zones.\n"); + } else { + size_t loaded = knot_zonedb_zone_count(db_new); + log_server_info("Loaded %zu out of %d zones.\n", + loaded, conf->zones_count); + if (loaded != conf->zones_count) { + log_server_warning("Not all the zones were loaded.\n"); + } } /* Lock RCU to ensure none will deallocate any data under our hands. */ @@ -2172,8 +2109,8 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns, */ int ret = zones_remove_zones(db_new, *db_old); - /* Heal zonedb index. */ - hattrie_build_index(db_new->zone_tree); + /* Rebuild zone database search stack. */ + knot_zonedb_build_index(db_new); /* Unlock RCU, messing with any data will not affect us now */ rcu_read_unlock(); @@ -3025,21 +2962,18 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data) /* Update events scheduled for zone. */ rcu_read_lock(); - knot_zone_t **zones = (knot_zone_t **)knot_zonedb_zones(ns->zone_db); - if (zones == NULL) { - rcu_read_unlock(); - return KNOT_ENOMEM; - } + knot_zone_t *zone = NULL; + const knot_zone_t **zones = knot_zonedb_zones(ns->zone_db); /* REFRESH zones. */ for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - zones_schedule_refresh(zones[i], 0); /* Now. */ - zones_schedule_notify(zones[i]); + zone = (knot_zone_t *)zones[i]; + zones_schedule_refresh(zone, 0); /* Now. */ + zones_schedule_notify(zone); } /* Unlock RCU. */ rcu_read_unlock(); - free(zones); return KNOT_EOK; } diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 0df4a409c545b0d444e1904462946080c47756f8..48f1648402af1b1a29af7beb59d7b6f788aa58b7 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -3057,7 +3057,7 @@ knot_nameserver_t *knot_ns_create() // Create zone database structure dbg_ns("Creating Zone Database structure...\n"); - ns->zone_db = knot_zonedb_new(); + ns->zone_db = knot_zonedb_new(0); if (ns->zone_db == NULL) { ERR_ALLOC_FAILED; free(ns); diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c index 54bbd9810f60abc923a6a4df0a63dabebc6289fb..bf153bb5a190ec07e87121a3a92a3969fb0e2732 100644 --- a/src/libknot/zone/zonedb.c +++ b/src/libknot/zone/zonedb.c @@ -20,35 +20,103 @@ #include <urcu.h> +#include "common/binsearch.h" #include "libknot/common.h" #include "libknot/zone/zone.h" #include "libknot/zone/zonedb.h" #include "libknot/dname.h" +#include "libknot/util/wire.h" #include "libknot/zone/node.h" #include "libknot/util/debug.h" +/* Array sorter generator. */ +static int knot_zonedb_cmp(const knot_dname_t* d1, const knot_dname_t *d2); +#define ASORT_PREFIX(X) knot_zonedb_##X +#define ASORT_KEY_TYPE knot_zone_t* +#define ASORT_LT(x, y) (knot_zonedb_cmp((x)->name, (y)->name) < 0) +#include "common/array-sort.h" + +/* Defines */ +#define BSEARCH_THRESHOLD 8 /* >= N for which binary search is favoured */ + /*----------------------------------------------------------------------------*/ /* Non-API functions */ /*----------------------------------------------------------------------------*/ +/*! \brief Discard zone in zone database. */ +static void delete_zone_from_db(knot_zone_t *zone) +{ + synchronize_rcu(); + knot_zone_set_flag(zone, KNOT_ZONE_DISCARDED, 1); + knot_zone_release(zone); +} + +/*! \brief Zone database zone name compare function. */ +static int knot_zonedb_cmp(const knot_dname_t* d1, const knot_dname_t *d2) +{ + int a_labels = knot_dname_labels(d1, NULL); + int b_labels = knot_dname_labels(d2, NULL); + + /* Lexicographic order. */ + if (a_labels == b_labels) { + return knot_dname_cmp(d1, d2); + } + + /* Name with more labels goes first. */ + return b_labels - a_labels; +} + +/*! \brief Find an equal name in sorted array (binary search). */ +#define ZONEDB_CMP(arr,i,x) knot_zonedb_cmp(((arr)[i])->name, (x)) +static long knot_zonedb_binsearch(knot_zone_t **arr, unsigned count, + const knot_dname_t *name) +{ + return BIN_SEARCH_FIRST_EQ_CMP(arr, count, name, ZONEDB_CMP); +} + +/*! \brief Find an equal name in an array (linear search). + * \note Linear search uses simple name equality test which could be + * faster than canonical compare and therefore more efficient for + * smaller arrays. + */ +static long knot_zonedb_linear_search(knot_zone_t **arr, unsigned count, + const knot_dname_t *name) { + for (unsigned i = 0; i < count; ++i) { + if (knot_dname_is_equal(arr[i]->name, name)) { + return i; + } + } + return -1; +} + +/*! \brief Zone array search. */ +static long knot_zonedb_array_search(knot_zone_t **arr, unsigned count, + const knot_dname_t *name) +{ + if (count < BSEARCH_THRESHOLD) { + return knot_zonedb_linear_search(arr, count, name); + } else { + return knot_zonedb_binsearch(arr, count, name); + } +} + /*----------------------------------------------------------------------------*/ /* API functions */ /*----------------------------------------------------------------------------*/ -knot_zonedb_t *knot_zonedb_new() +knot_zonedb_t *knot_zonedb_new(unsigned size) { - knot_zonedb_t *db = - (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t)); + knot_zonedb_t *db = malloc(sizeof(knot_zonedb_t)); CHECK_ALLOC_LOG(db, NULL); - db->zone_tree = hattrie_create(); - if (db->zone_tree == NULL) { + memset(db, 0, sizeof(knot_zonedb_t)); + db->reserved = size; + db->array = malloc(size * sizeof(knot_zone_t*)); + if (db->array == NULL) { free(db); return NULL; } - db->zone_count = 0; - return db; } @@ -72,12 +140,12 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone) } } - /* Insert new record. */ - uint8_t lf[KNOT_DNAME_MAXLEN]; - knot_dname_lf(lf, zone->name, NULL); - *hattrie_get(db->zone_tree, (char*)lf+1, *lf) = zone; + /* Invalidate search index. */ + db->stack_height = 0; - db->zone_count++; + /* Create new record. */ + assert(db->count < db->reserved); /* Should be already checked. */ + db->array[db->count++] = zone; return ret; } @@ -87,57 +155,107 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone) knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db, const knot_dname_t *zone_name) { - /* Fetch if exists. */ - uint8_t lf[KNOT_DNAME_MAXLEN]; - knot_dname_lf(lf, zone_name, NULL); - - value_t *old_zone = hattrie_tryget(db->zone_tree, (char*)lf+1, *lf); - if (old_zone == NULL) + if (db == NULL || zone_name == NULL) { return NULL; - - /* Remove from db. */ - if (hattrie_del(db->zone_tree, (char*)lf+1, *lf) < 0) + } + + /* Find the possible zone to remove. */ + int pos = knot_zonedb_array_search(db->array, db->count, zone_name); + if (pos < 0) { return NULL; + } - --db->zone_count; - return (knot_zone_t *)*old_zone; + /* Invalidate search index. */ + db->stack_height = 0; + + /* Move rest of the array to not break the ordering. */ + knot_zone_t *removed_zone = db->array[pos]; + unsigned remainder = (db->count - (pos + 1)) * sizeof(knot_zone_t*); + memmove(db->array + pos, db->array + pos + 1, remainder); + --db->count; + + return removed_zone; } /*----------------------------------------------------------------------------*/ -knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db, +int knot_zonedb_build_index(knot_zonedb_t *db) +{ + /* First, sort all zones based on the label count first and lexicographic + * order second. The name with most labels goes first. + * i.e. {a, a.b, a.c, b } -> {a.b, a.c, a, b} */ + knot_zonedb_sort(db->array, db->count); + + /* Scan the array and group names with the same label count together. */ + unsigned prev_label_count = 0; + unsigned current_label_count = 0; + knot_zone_t **endp = db->array + db->count; + knot_zonedb_stack_t *stack_top = db->stack - 1; /* Before actual stack. */ + db->stack_height = 0; + + for (knot_zone_t **zone = db->array; zone != endp; ++zone) { + /* Insert into current label count group. */ + current_label_count = knot_dname_labels((*zone)->name, NULL); + if (current_label_count == prev_label_count) { + ++stack_top->count; + continue; + } + + /* Begin new label count group. */ + ++stack_top; + ++db->stack_height; + stack_top->count = 1; + stack_top->labels = current_label_count; + stack_top->array = zone; + prev_label_count = current_label_count; + + } + + return KNOT_EOK; +} + +/*----------------------------------------------------------------------------*/ + +knot_zone_t *knot_zonedb_find_zone(knot_zonedb_t *db, const knot_dname_t *zone_name) { - uint8_t lf[KNOT_DNAME_MAXLEN]; - knot_dname_lf(lf, zone_name, NULL); - value_t *val = hattrie_tryget(db->zone_tree, (char*)lf+1, *lf); - if (val) - return (knot_zone_t *)*val; + int pos = knot_zonedb_array_search(db->array, db->count, zone_name); + if (pos < 0) { + return NULL; + } - return NULL; + return db->array[pos]; } /*----------------------------------------------------------------------------*/ -const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, - const knot_dname_t *dname) +knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, + const knot_dname_t *zone_name) { - if (db == NULL || dname == NULL) { + int zone_labels = knot_dname_labels(zone_name, NULL); + if (db == NULL || zone_labels < 0) { return NULL; } + + /* Walk down the stack, from the most labels to least. */ + knot_zonedb_stack_t *sp = db->stack, *endp = db->stack + db->stack_height; + for (; sp != endp; ++sp) { + /* Inspect only zones with <= labels than zone_labels. */ + if (sp->labels > zone_labels) { + continue; + } + + /* Skip non-matched labels. */ + while (sp->labels < zone_labels) { + zone_name = knot_wire_next_label(zone_name, NULL); + --zone_labels; + } - uint8_t lf[KNOT_DNAME_MAXLEN]; - knot_dname_lf(lf, dname, NULL); - - /* Attempt to find longest matching prefix. - * as zones are stored as \x00com\x00lake\x00 - * the longest matching prefix is essentially a best match, - * as it is guaranteed that inserted names end in \x00 - */ - value_t *val = NULL; - int len = hattrie_find_lpr(db->zone_tree, (char*)lf+1, *lf, &val); - if (len > -1) { - return (const knot_zone_t *)*val; + /* Possible candidate, search the array. */ + int k = knot_zonedb_array_search(sp->array, sp->count, zone_name); + if (k > -1) { + return sp->array[k]; + } } return NULL; @@ -148,6 +266,7 @@ const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, const knot_dname_t *zone_name) { + if (db == NULL || zone_name == NULL) { return NULL; } @@ -164,98 +283,39 @@ knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, /*----------------------------------------------------------------------------*/ -knot_zonedb_t *knot_zonedb_copy(const knot_zonedb_t *db) -{ - knot_zonedb_t *db_new = - (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t)); - CHECK_ALLOC_LOG(db_new, NULL); - - db_new->zone_tree = hattrie_dup(db->zone_tree, NULL); - if (db_new->zone_tree == NULL) { - free(db_new); - return NULL; - } - - return db_new; -} - -/*----------------------------------------------------------------------------*/ - size_t knot_zonedb_zone_count(const knot_zonedb_t *db) { - return db->zone_count; -} - -/*----------------------------------------------------------------------------*/ - -struct knot_zone_db_tree_arg { - const knot_zone_t **zones; - size_t count; -}; - -/*----------------------------------------------------------------------------*/ - -static void save_zone_to_array(void *node, void *data) -{ - knot_zone_t *zone = (knot_zone_t *)node; - struct knot_zone_db_tree_arg *args = - (struct knot_zone_db_tree_arg *)data; - assert(data); - args->zones[args->count++] = zone; + return db->count; } /*----------------------------------------------------------------------------*/ const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db) { - struct knot_zone_db_tree_arg args; - args.zones = malloc(sizeof(knot_zone_t *) * db->zone_count); - args.count = 0; - CHECK_ALLOC_LOG(args.zones, NULL); - - hattrie_iter_t *i = hattrie_iter_begin(db->zone_tree, 1); - while(!hattrie_iter_finished(i)) { - save_zone_to_array(*hattrie_iter_val(i), &args); - hattrie_iter_next(i); + if (db == NULL) { + return NULL; } - hattrie_iter_free(i); - - assert(db->zone_count == args.count); - - return args.zones; + + return (const knot_zone_t **)db->array; } /*----------------------------------------------------------------------------*/ void knot_zonedb_free(knot_zonedb_t **db) { - hattrie_free((*db)->zone_tree); + free((*db)->array); free(*db); *db = NULL; } /*----------------------------------------------------------------------------*/ -static int delete_zone_from_db(value_t *node, void *data) -{ - UNUSED(data); - assert(node); - if (*node != NULL) { - knot_zone_t *zone = (knot_zone_t *)(*node); - synchronize_rcu(); - knot_zone_set_flag(zone, KNOT_ZONE_DISCARDED, 1); - knot_zone_release(zone); - *node = NULL; - } - - return KNOT_EOK; -} - void knot_zonedb_deep_free(knot_zonedb_t **db) { dbg_zonedb("Deleting zone db (%p).\n", *db); - hattrie_apply_rev((*db)->zone_tree, delete_zone_from_db, NULL); - hattrie_free((*db)->zone_tree); - free(*db); - *db = NULL; + for (unsigned i = 0; i < (*db)->count; ++i) { + delete_zone_from_db((*db)->array[i]); + } + + knot_zonedb_free(db); } diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h index e1cfa13ebc5d71fb43f4fcefe51b62b3aa3668ea..a3f72b63bcdbc3e0bfd3bdf53e1ec0d5899f8ace 100644 --- a/src/libknot/zone/zonedb.h +++ b/src/libknot/zone/zonedb.h @@ -30,20 +30,43 @@ #ifndef _KNOT_ZONEDB_H_ #define _KNOT_ZONEDB_H_ -#include "common/hattrie/hat-trie.h" #include "libknot/zone/zone.h" #include "libknot/zone/node.h" #include "libknot/dname.h" -/*! - * \brief Zone database structure. Contains all zones managed by the server. +/* + * Zone DB represents a list of managed zones. + * Hashing should be avoided as it is expensive when only a small number of + * zones is present (TLD case). Linear run-length algorithms or worse should + * be avoided as well, as the number of zones may be large. + * + * Use of string-based algorithms for suffix search is viable, but would require + * transformation each time a name is searched. That again would be a + * constant cost even if the number of zones would be small. + * + * Zone database structure is a stack of zones grouped by label count in + * descending order (root label not counted), therefore first match is the longest. + * Each stack level is sorted for convenient binary search. + * example: + * {3 labels, 2 items} => [ 'a.b.c', 'b.b.c' ] + * {2 labels, 1 items} => [ 'x.z' ] + * {1 labels, 2 items} => [ 'y', 'w' ] + * + * Stack is built on top of the sorted array of zones for direct access and + * less memory requirements. */ -struct knot_zonedb { - hattrie_t *zone_tree; /*!< AVL tree of zones. */ - size_t zone_count; -}; - -typedef struct knot_zonedb knot_zonedb_t; +typedef struct { + unsigned labels; + unsigned count; + knot_zone_t** array; +} knot_zonedb_stack_t; + +typedef struct { + unsigned count, reserved; + knot_zone_t **array; + unsigned stack_height; + knot_zonedb_stack_t stack[KNOT_DNAME_MAXLABELS]; +} knot_zonedb_t; /*----------------------------------------------------------------------------*/ @@ -53,7 +76,7 @@ typedef struct knot_zonedb knot_zonedb_t; * \return Pointer to the created zone database structure or NULL if an error * occured. */ -knot_zonedb_t *knot_zonedb_new(); +knot_zonedb_t *knot_zonedb_new(unsigned size); /*! * \brief Adds new zone to the database. @@ -69,15 +92,8 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone); /*! * \brief Removes the given zone from the database if it exists. * - * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames(). - * If it was not, it may leak some memory due to checks used in - * knot_rdata_deep_free(). - * * \param db Zone database to remove from. * \param zone_name Name of the zone to be removed. - * \param destroy_zone Set to <> 0 if you do want the function to destroy the - * zone after removing from zone database. Set to 0 - * otherwise. * * \retval KNOT_EOK * \retval KNOT_ENOZONE @@ -85,6 +101,18 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone); knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db, const knot_dname_t *zone_name); +/*! + * \brief Build zone stack for faster lookup. + * + * Zone stack structure is described in the knot_zonedb_t struct. + * + * \param db Zone database. + * \retval KNOT_EOK + * \retval KNOT_ENOMEM + * \retval KNOT_EINVAL + */ +int knot_zonedb_build_index(knot_zonedb_t *db); + /*! * \brief Finds zone exactly matching the given zone name. * @@ -94,8 +122,8 @@ knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db, * \return Zone with \a zone_name being the owner of the zone apex or NULL if * not found. */ -knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db, - const knot_dname_t *zone_name); +knot_zone_t *knot_zonedb_find_zone(knot_zonedb_t *db, + const knot_dname_t *zone_name); /*! @@ -107,8 +135,8 @@ knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db, * \retval Zone in which the domain name should be present or NULL if no such * zone is found. */ -const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, - const knot_dname_t *dname); +knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db, + const knot_dname_t *zone_name); knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db, const knot_dname_t *zone_name); @@ -127,10 +155,6 @@ void knot_zonedb_free(knot_zonedb_t **db); /*! * \brief Destroys and deallocates the whole zone database including the zones. * - * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames(). - * If it was not, it may leak some memory due to checks used in - * knot_rdata_deep_free(). - * * \param db Zone database to be destroyed. */ void knot_zonedb_deep_free(knot_zonedb_t **db);