]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Memory cleanup.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 30 Aug 2007 12:45:19 +0000 (12:45 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 30 Aug 2007 12:45:19 +0000 (12:45 +0000)
       - caught bad free of un-alloced data in worker_send error case.
       - memory accounting for key cache (trust anchors and temporary cache).
       - memory accounting fixup for outside network tcp pending waits.
       - memory accounting fixup for outside network tcp callbacks.
       - memory accounting for iterator fixed storage.
       - key cache size and slabs config options.

git-svn-id: file:///svn/unbound/trunk@566 be551aaa-1e26-0410-a405-d3ace91eadb9

23 files changed:
daemon/unbound.c
daemon/worker.c
doc/Changelog
doc/example.conf
doc/unbound.conf.5
iterator/iter_donotq.c
iterator/iter_donotq.h
iterator/iter_fwd.c
iterator/iter_fwd.h
iterator/iter_hints.c
iterator/iter_hints.h
iterator/iterator.c
services/outside_network.c
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
util/module.h
validator/val_anchor.c
validator/val_anchor.h
validator/val_kcache.c
validator/val_kcache.h
validator/validator.c

index 6ff00b1080dafab9727de8025db80c424d8df797..601863cbfbd9768c7700e097d3581c9e7c44376e 100644 (file)
@@ -396,5 +396,6 @@ main(int argc, char* argv[])
        }
 
        run_daemon(cfgfile, cmdline_verbose, debug_mode);
+       log_init(NULL); /* close logfile */
        return 0;
 }
index 91b993c4e2cb5cb4fe6d422ccd9987d146f3cb2e..4b768dded5f4a3aa9a626deac2d0842cdbc352b0 100644 (file)
@@ -113,8 +113,11 @@ debug_total_mem(size_t calctotal)
 void
 worker_mem_report(struct worker* worker, struct serviced_query* cur_serv)
 {
+       /* debug func in validator module */
+       size_t val_kcache_get_mem(void*);
        size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
-       size_t me;
+       size_t me, iter, val;
+       int i;
        if(verbosity < VERB_ALGO) 
                return;
        front = listen_get_mem(worker->front);
@@ -125,20 +128,32 @@ worker_mem_report(struct worker* worker, struct serviced_query* cur_serv)
        mesh = mesh_get_mem(worker->env.mesh);
        ac = alloc_get_mem(&worker->alloc);
        superac = alloc_get_mem(&worker->daemon->superalloc);
+       iter = 0;
+       val = 0;
+       for(i=0; i<worker->env.mesh->num_modules; i++) {
+               if(strcmp(worker->env.mesh->modfunc[i]->name, "validator")==0)
+                       val += (*worker->env.mesh->modfunc[i]->get_mem)
+                               (&worker->env, i);
+               else    iter += (*worker->env.mesh->modfunc[i]->get_mem)
+                               (&worker->env, i);
+       }
        me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
                + comm_point_get_mem(worker->cmd_com) + 
                sizeof(worker->rndstate) + region_get_mem(worker->scratchpad)+
                sizeof(*worker->env.scratch_buffer) + 
                ldns_buffer_capacity(worker->env.scratch_buffer);
-       if(cur_serv)
+       if(cur_serv) {
+               log_info("cur_serv = %d", (int)serviced_get_mem(cur_serv));
                me += serviced_get_mem(cur_serv);
-       total = front+back+mesh+msg+rrset+infra+ac+superac+me;
+       }
+       total = front+back+mesh+msg+rrset+infra+iter+val+ac+superac+me;
        log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
-               "rrset=%u infra=%u alloccache=%u globalalloccache=%u me=%u",
+               "rrset=%u infra=%u iter=%u val=%u "
+               "alloccache=%u globalalloccache=%u me=%u",
                (unsigned)total, (unsigned)front, (unsigned)back, 
                (unsigned)mesh, (unsigned)msg, (unsigned)rrset, 
-               (unsigned)infra, (unsigned)ac, (unsigned)superac, 
-               (unsigned)me);
+               (unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)ac, 
+               (unsigned)superac, (unsigned)me);
        debug_total_mem(total);
 }
 
@@ -1027,7 +1042,6 @@ worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
                worker_handle_service_reply, e, worker->back->udp_buff,
                &outbound_entry_compare);
        if(!e->qsent) {
-               free(e);
                return NULL;
        }
        return e;
index b1dbdbb04b783189cd7aefc9098785b194501f47..a4dee8686183271341abfe18f4dcd5862467d5cf 100644 (file)
@@ -1,6 +1,12 @@
 30 August 2007: Wouter
        - fixup override date config option.
        - config options to control memory usage.
+       - caught bad free of un-alloced data in worker_send error case.
+       - memory accounting for key cache (trust anchors and temporary cache).
+       - memory accounting fixup for outside network tcp pending waits.
+       - memory accounting fixup for outside network tcp callbacks.
+       - memory accounting for iterator fixed storage.
+       - key cache size and slabs config options.
 
 29 August 2007: Wouter
        - test tool to sign rrsets for testing validator with.
index 8ee8ade1bcec0e4e49921baa41684906c8fe6b2f..7aad2b2a8a3c3436faa2176024477b5b823d8aa6 100644 (file)
@@ -192,6 +192,16 @@ server:
        # replies if the message is found secure. The default is off.
        # val-permissive-mode: no
 
+       # the amount of memory to use for the key cache.
+       # in bytes. default is 4 Mb
+       # key-cache-size: 4194304
+
+       # the number of slabs to use for the key cache.
+       # the number of slabs must be a power of 2.
+       # more slabs reduce lock contention, but fragment memory usage.
+       # key-cache-slabs: 4
+
+
 # Stub zones.
 # Create entries like below, to make all queries for 'example.com' and 
 # 'example.org' go to the given list of nameservers. list zero or more 
index b8534832111ee815236c6fdc84f0fc868450f67e..25b5890bd0c90b4aadbb62aec6496c644009f95a 100644 (file)
@@ -232,6 +232,12 @@ reply is not withheld from the client with SERVFAIL as usual. The client
 receives the bogus data. For messages that are found to be secure the AD bit 
 is set in replies. Also logging is performed as for full validation.
 The default value is "no". 
+.It \fBkey-cache-size:\fR <number>
+Number of bytes size of the key cache. Default is 4 megabytes.
+.It \fBkey-cache-slabs:\fR <number>
+Number of slabs in the key cache. Slabs reduce lock contention by threads.
+Must be set to a power of 2. Setting (close) to the number of cpus is a 
+reasonable guess.
 .El
 
 .Ss Stub Zone Options
@@ -289,6 +295,8 @@ server:
        rrset-cache-slabs: 1
        infra-cache-numhosts: 200
        infra-cache-numlame: 10
+       key-cache-size: 102400  # 100 Kb.
+       key-cache-slabs: 1
        num-queries-per-thread: 30
        target-fetch-policy: "2 1 0 0 0 0"
        harden-large-queries: "yes"
index 593ae0ffabd39e3c04b1a6272b0deb89c30f528f..bfd39ef483bd6a1faf263dfd02aeb3fb26028e12 100644 (file)
@@ -147,3 +147,10 @@ donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
                return 1;
        return 0;
 }
+
+size_t 
+donotq_get_mem(struct iter_donotq* donotq)
+{
+       if(!donotq) return 0;
+       return sizeof(*donotq) + region_get_mem(donotq->region);
+}
index 70813f2554f801547f02fae4108bfce1341e703a..ffa337801c5c936e6eebc8ce8b84f9d9c241d5d8 100644 (file)
@@ -103,4 +103,12 @@ int donotq_apply_cfg(struct iter_donotq* donotq, struct config_file* cfg);
 int donotq_lookup(struct iter_donotq* donotq, struct sockaddr_storage* addr,
        socklen_t addrlen);
 
+/**
+ * Get memory used by donotqueryaddresses structure.
+ * @param donotq: structure for address storage.
+ * @return bytes in use.
+ */
+size_t donotq_get_mem(struct iter_donotq* donotq);
+
+
 #endif /* ITERATOR_ITER_DONOTQ_H */
index d9646a05ee0aec5e66af4be19cd33b69bf492248..2a964dfe1723da4772acf2b6a19c03725485a283 100644 (file)
@@ -283,3 +283,11 @@ forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass)
                return result->dp;
        return NULL;
 }
+
+size_t 
+forwards_get_mem(struct iter_forwards* fwd)
+{
+       if(!fwd)
+               return 0;
+       return sizeof(*fwd) + region_get_mem(fwd->region);
+}
index 3d992b4e6379fd03b67949ab3ec8a54d71f5e58c..a288fc649d099631335cb24d22a771cf25b9fae8 100644 (file)
@@ -118,4 +118,11 @@ int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg);
 struct delegpt* forwards_lookup(struct iter_forwards* fwd, 
        uint8_t* qname, uint16_t qclass);
 
+/**
+ * Get memory in use by forward storage
+ * @param fwd: forward storage.
+ * @return bytes in use
+ */
+size_t forwards_get_mem(struct iter_forwards* fwd);
+
 #endif /* ITERATOR_ITER_FWD_H */
index 25df77d7e3f6c95dfe6687cc94100a89f3afdbdd..4530f3fd9d3f4b70d7ca43e7a6d5e953d5d7e2f9 100644 (file)
@@ -375,3 +375,10 @@ hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
                return result->dp; /* need to prime this stub */
        return NULL;
 }
+
+size_t 
+hints_get_mem(struct iter_hints* hints)
+{
+       if(!hints) return 0;
+       return sizeof(*hints) + region_get_mem(hints->region);
+}
index 36d01b00d13dd916542e829ed1c40dabf3901b0f..582acd299645eb835d8e5562d06a5b0ef858d999 100644 (file)
@@ -127,4 +127,11 @@ struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass);
 struct delegpt* hints_lookup_stub(struct iter_hints* hints, 
        uint8_t* qname, uint16_t qclass, struct delegpt* dp);
 
+/**
+ * Get memory in use by hints
+ * @param hints: hint storage.
+ * @return bytes in use
+ */
+size_t hints_get_mem(struct iter_hints* hints);
+
 #endif /* ITERATOR_ITER_HINTS_H */
index 6d63d6c6c2381262f11de24a8c01e01def50cc0f..8ad0ed2cfb95e08937e343817fed3c6269a54bd9 100644 (file)
@@ -1591,13 +1591,24 @@ iter_clear(struct module_qstate* qstate, int id)
        qstate->minfo[id] = NULL;
 }
 
+/** iterator alloc size routine */
+static size_t iter_get_mem(struct module_env* env, int id)
+{
+       struct iter_env* ie = (struct iter_env*)env->modinfo[id];
+       if(!ie)
+               return 0;
+       return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
+               + hints_get_mem(ie->hints) + forwards_get_mem(ie->fwds)
+               + donotq_get_mem(ie->donotq);
+}
+
 /**
  * The iterator function block 
  */
 static struct module_func_block iter_block = {
        "iterator",
        &iter_init, &iter_deinit, &iter_operate, &iter_inform_super, 
-       &iter_clear
+       &iter_clear, &iter_get_mem
 };
 
 struct module_func_block* 
index 14c969dca8c4cb1202ca37c1000e9757eb1ccd13..6fee1fc13a6ced4f17807d029bdcf94bdf3b0d5e 100644 (file)
@@ -120,7 +120,7 @@ waiting_tcp_delete(struct waiting_tcp* w)
 
 /** use next free buffer to service a tcp query */
 static int
-outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
+outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
 {
        struct pending_tcp* pend = w->outnet->tcp_free;
        int s;
@@ -154,7 +154,7 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
        pend->next_free = NULL;
        pend->query = w;
        ldns_buffer_clear(pend->c->buffer);
-       ldns_buffer_write(pend->c->buffer, pkt, w->pkt_len);
+       ldns_buffer_write(pend->c->buffer, pkt, pkt_len);
        ldns_buffer_flip(pend->c->buffer);
        pend->c->tcp_is_reading = 0;
        pend->c->tcp_byte_count = 0;
@@ -172,7 +172,7 @@ use_free_buffer(struct outside_network* outnet)
                outnet->tcp_wait_first = w->next_waiting;
                if(outnet->tcp_wait_last == w)
                        outnet->tcp_wait_last = NULL;
-               if(!outnet_tcp_take_into_use(w, w->pkt)) {
+               if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) {
                        (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
                        waiting_tcp_delete(w);
                }
@@ -759,7 +759,7 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
                return NULL;
        }
        w->pkt = NULL;
-       w->pkt_len = ldns_buffer_limit(packet);
+       w->pkt_len = 0;
        /* id uses lousy random() TODO use better and entropy */
        id = ((unsigned)ub_random(rnd)>>8) & 0xffff;
        LDNS_ID_SET(ldns_buffer_begin(packet), id);
@@ -773,13 +773,15 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
        comm_timer_set(w->timer, &tv);
        if(pend) {
                /* we have a buffer available right now */
-               if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet))) {
+               if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet),
+                       ldns_buffer_limit(packet))) {
                        waiting_tcp_delete(w);
                        return NULL;
                }
        } else {
                /* queue up */
                w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
+               w->pkt_len = ldns_buffer_limit(packet);
                memmove(w->pkt, ldns_buffer_begin(packet), w->pkt_len);
                w->next_waiting = NULL;
                if(outnet->tcp_wait_last)
@@ -1268,16 +1270,19 @@ serviced_get_mem(struct serviced_query* sq)
        s = sizeof(*sq) + sq->qbuflen;
        for(sb = sq->cblist; sb; sb = sb->next)
                s += sizeof(*sb);
-       /* always sq->pending existed, but is null to delete after callback */
        if(sq->status == serviced_query_UDP_EDNS ||
                sq->status == serviced_query_UDP) {
                s += sizeof(struct pending);
                s += comm_timer_get_mem(NULL);
        } else {
                /* does not have size of the pkt pointer */
+               /* always has a timer except on malloc failures */
+
+               /* these sizes are part of the main outside network mem */
+               /*
                s += sizeof(struct waiting_tcp);
-               /* always has a timer expect on malloc failures */
                s += comm_timer_get_mem(NULL);
+               */
        }
        return s;
 }
index 31db0a7704159a10f8f3b237c24cdc18011645f6..0756d36701f01849c74289b8f4e62c7360d84d14 100644 (file)
@@ -119,6 +119,8 @@ config_create()
        cfg->val_date_override = 0;
        cfg->val_clean_additional = 1;
        cfg->val_permissive_mode = 0;
+       cfg->key_cache_size = 4 * 1024 * 1024;
+       cfg->key_cache_slabs = 4;
        if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
        return cfg;
 error_exit:
index c4bf9208e76bda09314067033e3ae29e14335e1c..6226d9f0cf8e29fb70fcb2b61953fc291a3c5067 100644 (file)
@@ -157,6 +157,11 @@ struct config_file {
        /** should validator allow bogus messages to go through */
        int val_permissive_mode;
 
+       /** size of the key cache */
+       size_t key_cache_size;
+       /** slabs in the key cache. */
+       size_t key_cache_slabs;
+
        /** daemonize, i.e. fork into the background. */
        int do_daemonize;
 };
index 08443d635e4a7e0da38a310f5d3cb57fc846788a..6a4e5a697d59b5b7931e0292e22be2f851dac364 100644 (file)
@@ -150,6 +150,8 @@ val-override-date{COLON}    { YDOUT; return VAR_VAL_OVERRIDE_DATE;}
 val-bogus-ttl{COLON}   { YDOUT; return VAR_BOGUS_TTL;}
 val-clean-additional{COLON}    { YDOUT; return VAR_VAL_CLEAN_ADDITIONAL;}
 val-permissive-mode{COLON}     { YDOUT; return VAR_VAL_PERMISSIVE_MODE;}
+key-cache-size{COLON}  { YDOUT; return VAR_KEY_CACHE_SIZE;}
+key-cache-slabs{COLON} { YDOUT; return VAR_KEY_CACHE_SLABS;}
 {NEWLINE}              { LEXOUT(("NL\n")); cfg_parser->line++;}
 
        /* Quoted strings. Strip leading and ending quotes */
index 75bfb438ad95cd0da91225e466c9c51278bfd17d..4a16e08ccd4c7c7c4c8f8103b25fa66838ae267c 100644 (file)
@@ -82,7 +82,8 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE VAR_MODULE_CONF
 %token VAR_TRUST_ANCHOR_FILE VAR_TRUST_ANCHOR VAR_VAL_OVERRIDE_DATE
 %token VAR_BOGUS_TTL VAR_VAL_CLEAN_ADDITIONAL VAR_VAL_PERMISSIVE_MODE
-%token VAR_INCOMING_NUM_TCP VAR_MSG_BUFFER_SIZE
+%token VAR_INCOMING_NUM_TCP VAR_MSG_BUFFER_SIZE VAR_KEY_CACHE_SIZE
+%token VAR_KEY_CACHE_SLABS
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -118,7 +119,8 @@ content_server: server_num_threads | server_verbosity | server_port |
        server_harden_glue | server_module_conf | server_trust_anchor_file |
        server_trust_anchor | server_val_override_date | server_bogus_ttl |
        server_val_clean_additional | server_val_permissive_mode |
-       server_incoming_num_tcp | server_msg_buffer_size
+       server_incoming_num_tcp | server_msg_buffer_size | 
+       server_key_cache_size | server_key_cache_slabs
        ;
 stubstart: VAR_STUB_ZONE
        {
@@ -557,6 +559,28 @@ server_val_permissive_mode: VAR_VAL_PERMISSIVE_MODE STRING
                free($2);
        }
        ;
+server_key_cache_size: VAR_KEY_CACHE_SIZE STRING
+       {
+               OUTYY(("P(server_key_cache_size:%s)\n", $2));
+               if(atoi($2) == 0)
+                       yyerror("number expected");
+               else cfg_parser->cfg->key_cache_size = atoi($2);
+               free($2);
+       }
+       ;
+server_key_cache_slabs: VAR_KEY_CACHE_SLABS STRING
+       {
+               OUTYY(("P(server_key_cache_slabs:%s)\n", $2));
+               if(atoi($2) == 0)
+                       yyerror("number expected");
+               else {
+                       cfg_parser->cfg->key_cache_slabs = atoi($2);
+                       if(!is_pow2(cfg_parser->cfg->key_cache_slabs))
+                               yyerror("must be a power of 2");
+               }
+               free($2);
+       }
+       ;
 stub_name: VAR_NAME STRING
        {
                OUTYY(("P(name:%s)\n", $2));
index 1115d36df063b59cbc5aea9589ca0385275e9453..83b1431bf8ec0d4306c07b2d6d61310b920f301c 100644 (file)
@@ -283,6 +283,7 @@ struct module_func_block {
         * return: 0 on error
         */
        int (*init)(struct module_env* env, int id);
+
        /**
         * de-init, delete, the module. Called once for the global state.
         * @param env: module environment.
@@ -329,6 +330,14 @@ struct module_func_block {
         * clear module specific data
         */
        void (*clear)(struct module_qstate* qstate, int id);
+
+       /**
+        * How much memory is the module specific data using. 
+        * @param env: module environment.
+        * @param id: the module id.
+        * @return the number of bytes that are alloced.
+        */
+       size_t (*get_mem)(struct module_env* env, int id);
 };
 
 /** 
index 51ebe85c6d0d13da774ea219236cad45287bde0a..f6b8e3d3dcfdbdbf958de22c6d4cceffb589c793 100644 (file)
@@ -525,3 +525,9 @@ anchors_lookup(struct val_anchors* anchors,
        }
        return result;
 }
+
+size_t 
+anchors_get_mem(struct val_anchors* anchors)
+{
+       return sizeof(*anchors) + region_get_mem(anchors->region);
+}
index 01c3e801d96e240e274bdd8f4bb2b6aaa3058266..934f8302e4a71a3f855f8b0fdd5419d48672248e 100644 (file)
@@ -151,4 +151,11 @@ struct trust_anchor* anchors_lookup(struct val_anchors* anchors,
 int anchor_store_str(struct val_anchors* anchors, ldns_buffer* buffer,
        const char* str);
 
+/**
+ * Get memory in use by the trust anchor storage
+ * @param anchors: anchor storage.
+ * @return memory in use in bytes.
+ */
+size_t anchors_get_mem(struct val_anchors* anchors);
+
 #endif /* VALIDATOR_VAL_ANCHOR_H */
index 5635e4266318a224e1b6072aaf66aa9fd78f67ef..56cef3f397edd1422393a282b5bfac4f2ff46fa2 100644 (file)
@@ -55,10 +55,9 @@ key_cache_create(struct config_file* cfg)
                log_err("malloc failure");
                return NULL;
        }
-       (void)cfg; /* TODO use config for keycache params */
-       numtables = HASH_DEFAULT_SLABS;
+       numtables = cfg->key_cache_slabs;
        start_size = HASH_DEFAULT_STARTARRAY;
-       maxmem = HASH_DEFAULT_MAXMEM;
+       maxmem = cfg->key_cache_size;
        kcache->slab = slabhash_create(numtables, start_size, maxmem,
                &key_entry_sizefunc, &key_entry_compfunc,
                &key_entry_delkeyfunc, &key_entry_deldatafunc, NULL);
@@ -147,3 +146,10 @@ key_cache_obtain(struct key_cache* kcache, uint8_t* name, size_t namelen,
        }
        return NULL;
 }
+
+size_t 
+key_cache_get_mem(struct key_cache* kcache)
+{
+       return sizeof(*kcache) + slabhash_get_mem(kcache->slab);
+}
+
index 2f1d3af6a3bfecf74244efa4548b50a48090f689..98fb9097bf34b1077d96907547ce0369f8a79e5a 100644 (file)
@@ -94,4 +94,11 @@ struct key_entry_key* key_cache_obtain(struct key_cache* kcache,
        uint8_t* name, size_t namelen, uint16_t key_class, 
        struct region* region);
 
+/**
+ * Get memory in use by the key cache.
+ * @param kcache: the key cache.
+ * @return memory in use in bytes.
+ */
+size_t key_cache_get_mem(struct key_cache* kcache);
+
 #endif /* VALIDATOR_VAL_KCACHE_H */
index f4af58f23e46298b2b1112f256d002294955aa06..15c7ef44fcbee35043af4105f4f2d92ef7078cca 100644 (file)
@@ -1774,12 +1774,29 @@ val_clear(struct module_qstate* qstate, int id)
        qstate->minfo[id] = NULL;
 }
 
+/**
+ * Debug helper routine that assists worker in determining memory in 
+ * use.
+ * @param me: mod_env value
+ * @return memory in use in bytes.
+ */
+static size_t 
+val_get_mem(struct module_env* env, int id)
+{
+       struct val_env* ve = (struct val_env*)env->modinfo[id];
+       if(!ve)
+               return 0;
+       return sizeof(*ve) + key_cache_get_mem(ve->kcache) + 
+               anchors_get_mem(ve->anchors);
+}
+
 /**
  * The validator function block 
  */
 static struct module_func_block val_block = {
        "validator",
-       &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear
+       &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear,
+       &val_get_mem
 };
 
 struct module_func_block* 
@@ -1799,3 +1816,4 @@ val_state_to_string(enum val_state state)
        }
        return "UNKNOWN VALIDATOR STATE";
 }
+