From: Wouter Wijngaards Date: Tue, 15 May 2007 10:53:27 +0000 (+0000) Subject: Host cache code. X-Git-Tag: release-0.4~147 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e6dccd85656cd00710b564d60ef65dbbd96b563d;p=thirdparty%2Funbound.git Host cache code. git-svn-id: file:///svn/unbound/trunk@319 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 74d58f281..5a7e9a61d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +15 May 2007: Wouter + - host cache code. + 14 May 2007: Wouter - Port to OS/X and Dec Alpha. Printf format and alignment fixes. - extensive lock debug report on join timeout. diff --git a/services/cache/infra.c b/services/cache/infra.c index 356dc690d..da33421b2 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -40,4 +40,393 @@ */ #include "config.h" #include "services/cache/infra.h" +#include "util/storage/slabhash.h" +#include "util/storage/lookup3.h" +#include "util/log.h" +#include "util/net_help.h" +/** calculate size for the hashtable, does not count size of lameness, + * so the hashtable is a fixed number of items */ +static size_t +infra_host_sizefunc(void* ATTR_UNUSED(k), void* ATTR_UNUSED(d)) +{ + return sizeof(struct infra_host_key) + sizeof(struct infra_host_data); +} + +/** compare two addresses, returns -1, 0, or +1 */ +static int +infra_host_compfunc(void* key1, void* key2) +{ + struct infra_host_key* k1 = (struct infra_host_key*)key1; + struct infra_host_key* k2 = (struct infra_host_key*)key2; + if(k1->addrlen != k2->addrlen) { + if(k1->addrlen < k2->addrlen) + return -1; + return 1; + } + return memcmp(&k1->addr, &k2->addr, k1->addrlen); +} + +/** delete key, and destroy the lock */ +static void +infra_host_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) +{ + struct infra_host_key* key = (struct infra_host_key*)k; + if(!key) + return; + lock_rw_destroy(&key->entry.lock); + free(key); +} + +/** delete data and destroy the lameness hashtable */ +static void +infra_host_deldatafunc(void* d, void* ATTR_UNUSED(arg)) +{ + struct infra_host_data* data = (struct infra_host_data*)d; + lruhash_delete(data->lameness); + free(data); +} + +struct slabhash* +infra_create(struct config_file* cfg) +{ + /* TODO: use config settings */ + /* the size of the lameness tables are not counted */ + size_t maxmem = HOST_DEFAULT_SIZE * (sizeof(struct infra_host_key) + + sizeof(struct infra_host_data)); + struct slabhash* infra = slabhash_create(HASH_DEFAULT_SLABS, + INFRA_HOST_STARTSIZE, maxmem, &infra_host_sizefunc, + &infra_host_compfunc, &infra_host_delkeyfunc, + &infra_host_deldatafunc, NULL); + return infra; +} + +void +infra_delete(struct slabhash* infra) +{ + if(!infra) + return; + slabhash_delete(infra); +} + +/** calculate the hash value for a host key */ +static hashvalue_t +hash_addr(struct sockaddr_storage* addr, socklen_t addrlen) +{ + hashvalue_t h = 0xab; + h = hashlittle(&addrlen, sizeof(addrlen), h); + h = hashlittle(addr, addrlen, h); + return h; +} + +/** lookup version that does not check host ttl (you check it) */ +static struct lruhash_entry* +infra_lookup_host_nottl(struct slabhash* infra, + struct sockaddr_storage* addr, socklen_t addrlen, int wr) +{ + struct infra_host_key k; + k.addrlen = addrlen; + memcpy(&k.addr, addr, addrlen); + k.entry.hash = hash_addr(addr, addrlen); + k.entry.key = (void*)&k; + k.entry.data = NULL; + return slabhash_lookup(infra, k.entry.hash, &k, wr); +} + +struct infra_host_data* +infra_lookup_host(struct slabhash* infra, + struct sockaddr_storage* addr, socklen_t addrlen, int wr, + time_t timenow, struct infra_host_key** key) +{ + struct infra_host_data* data; + struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr, + addrlen, wr); + *key = NULL; + if(!e) + return NULL; + /* check TTL */ + data = (struct infra_host_data*)e->data; + if(data->ttl < timenow) { + lock_rw_unlock(&e->lock); + return NULL; + } + *key = (struct infra_host_key*)e->key; + return data; +} + +/** + * Create and init a new entry for a host + * @param addr: host address. + * @param addrlen: length of addr. + * @param tm: time now. + * @return: the new entry or NULL on malloc failure. + */ +static struct lruhash_entry* +new_host_entry(struct sockaddr_storage* addr, socklen_t addrlen, time_t tm) +{ + struct infra_host_data* data; + struct infra_host_key* key = (struct infra_host_key*)malloc( + sizeof(struct infra_host_key)); + if(!key) + return NULL; + data = (struct infra_host_data*)malloc( + sizeof(struct infra_host_data)); + if(!data) { + free(key); + return NULL; + } + lock_rw_init(&key->entry.lock); + key->entry.hash = hash_addr(addr, addrlen); + key->entry.key = (void*)key; + key->entry.data = (void*)data; + key->addrlen = addrlen; + memcpy(&key->addr, addr, addrlen); + data->ttl = tm + HOST_TTL; + data->lameness = NULL; + data->edns_version = 0; + rtt_init(&data->rtt); + return &key->entry; +} + +int +infra_host(struct slabhash* infra, struct sockaddr_storage* addr, + socklen_t addrlen, time_t timenow, int* edns_vs, int* to) +{ + struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr, + addrlen, 0); + struct infra_host_data* data; + if(e && ((struct infra_host_data*)e->data)->ttl < timenow) { + /* it expired, try to reuse existing entry */ + lock_rw_unlock(&e->lock); + e = infra_lookup_host_nottl(infra, addr, addrlen, 1); + if(e) { + /* if its still there we have a writelock, init */ + /* re-initialise */ + data = (struct infra_host_data*)e->data; + data->ttl = timenow + HOST_TTL; + rtt_init(&data->rtt); + /* do not touch lameness, it may be valid still */ + data->edns_version = 0; + } + } + if(!e) { + /* insert new entry */ + if(!(e = new_host_entry(addr, addrlen, timenow))) + return 0; + data = (struct infra_host_data*)e->data; + *to = rtt_timeout(&data->rtt); + *edns_vs = data->edns_version; + slabhash_insert(infra, e->hash, e, data, NULL); + return 1; + } + /* use existing entry */ + data = (struct infra_host_data*)e->data; + *to = rtt_timeout(&data->rtt); + *edns_vs = data->edns_version; + lock_rw_unlock(&e->lock); + return 1; +} + +/** hash lameness key */ +static hashvalue_t +hash_lameness(uint8_t* name, size_t namelen) +{ + return hashlittle(name, namelen, 0xab); +} + +int +infra_lookup_lame(struct infra_host_data* host, + uint8_t* name, size_t namelen, time_t timenow) +{ + struct lruhash_entry* e; + struct infra_lame_key k; + struct infra_lame_data *d; + if(!host->lameness) + return 0; + k.entry.hash = hash_lameness(name, namelen); + k.zonename = name; + k.namelen = namelen; + k.entry.key = (void*)&k; + k.entry.data = NULL; + e = lruhash_lookup(host->lameness, k.entry.hash, &k, 0); + if(!e) + return 0; + d = (struct infra_lame_data*)e->data; + if(d->ttl < timenow) { + lock_rw_unlock(&e->lock); + return 0; + } + lock_rw_unlock(&e->lock); + return 1; +} + +/** calculate size, which is fixed, zonename does not count so that + * a fixed number of items is stored */ +static size_t +infra_lame_sizefunc(void* ATTR_UNUSED(k), void* ATTR_UNUSED(d)) +{ + return sizeof(struct infra_lame_key)+sizeof(struct infra_lame_data); +} + +/** compare zone names, returns -1, 0, +1 */ +static int +infra_lame_compfunc(void* key1, void* key2) +{ + struct infra_lame_key* k1 = (struct infra_lame_key*)key1; + struct infra_lame_key* k2 = (struct infra_lame_key*)key2; + if(k1->namelen != k2->namelen) { + if(k1->namelen < k2->namelen) + return -1; + return 1; + } + return memcmp(k1->zonename, k2->zonename, k1->namelen); +} + +/** free key, lock and zonename */ +static void +infra_lame_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) +{ + struct infra_lame_key* key = (struct infra_lame_key*)k; + if(!key) + return; + lock_rw_destroy(&key->entry.lock); + free(key->zonename); + free(key); +} + +/** free the lameness data */ +static void +infra_lame_deldatafunc(void* d, void* ATTR_UNUSED(arg)) +{ + if(!d) + return; + free(d); +} + +int +infra_set_lame(struct slabhash* infra, + struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* name, size_t namelen, time_t timenow) +{ + struct infra_host_data* data; + struct lruhash_entry* e; + int needtoinsert = 0; + struct infra_lame_key* k; + struct infra_lame_data* d; + /* allocate at start, easier cleanup (no locks held) */ + k = (struct infra_lame_key*)malloc(sizeof(*k)); + if(!k) { + log_err("set_lame: malloc failure"); + return 0; + } + d = (struct infra_lame_data*)malloc(sizeof(*d)); + if(!d) { + free(k); + log_err("set_lame: malloc failure"); + return 0; + } + k->zonename = memdup(name, namelen); + if(!k->zonename) { + free(d); + free(k); + log_err("set_lame: malloc failure"); + return 0; + } + lock_rw_init(&k->entry.lock); + k->entry.hash = hash_lameness(name, namelen); + k->entry.key = (void*)k; + k->entry.data = (void*)d; + d->ttl = timenow + HOST_LAME_TTL; + k->namelen = namelen; + e = infra_lookup_host_nottl(infra, addr, addrlen, 1); + if(!e) { + /* insert it */ + if(!(e = new_host_entry(addr, addrlen, timenow))) { + free(k->zonename); + free(k); + free(d); + log_err("set_lame: malloc failure"); + return 0; + } + needtoinsert = 1; + } + /* got an entry, now set the zone lame */ + data = (struct infra_host_data*)e->data; + if(!data->lameness) { + /* create hash table if not there already */ + data->lameness = lruhash_create(INFRA_LAME_STARTSIZE, + INFRA_LAME_MAXMEM*(sizeof(struct infra_lame_key)+ + sizeof(struct infra_lame_data)), infra_lame_sizefunc, + infra_lame_compfunc, infra_lame_delkeyfunc, + infra_lame_deldatafunc, NULL); + if(!data->lameness) { + log_err("set_lame: malloc failure"); + if(needtoinsert) slabhash_insert(infra, e->hash, e, + e->data, NULL); + else lock_rw_unlock(&e->lock); + free(k->zonename); + free(k); + free(d); + return 0; + } + } + /* inserts new entry, or updates TTL of older entry */ + lruhash_insert(data->lameness, k->entry.hash, &k->entry, d, NULL); + + if(needtoinsert) + slabhash_insert(infra, e->hash, e, e->data, NULL); + else lock_rw_unlock(&e->lock); + return 1; +} + +int +infra_rtt_update(struct slabhash* infra, + struct sockaddr_storage* addr, socklen_t addrlen, + int roundtrip, time_t timenow) +{ + struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr, + addrlen, 1); + struct infra_host_data* data; + int needtoinsert = 0; + if(!e) { + if(!(e = new_host_entry(addr, addrlen, timenow))) + return 0; + needtoinsert = 1; + } + /* have an entry, update the rtt, and the ttl */ + data = (struct infra_host_data*)e->data; + data->ttl = timenow + HOST_TTL; + if(roundtrip == -1) + rtt_lost(&data->rtt); + else rtt_update(&data->rtt, roundtrip); + + if(needtoinsert) + slabhash_insert(infra, e->hash, e, e->data, NULL); + else lock_rw_unlock(&e->lock); + return 1; +} + +int +infra_edns_update(struct slabhash* infra, + struct sockaddr_storage* addr, socklen_t addrlen, + int edns_version, time_t timenow) +{ + struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr, + addrlen, 1); + struct infra_host_data* data; + int needtoinsert = 0; + if(!e) { + if(!(e = new_host_entry(addr, addrlen, timenow))) + return 0; + needtoinsert = 1; + } + /* have an entry, update the rtt, and the ttl */ + data = (struct infra_host_data*)e->data; + data->ttl = timenow + HOST_TTL; + data->edns_version = edns_version; + + if(needtoinsert) + slabhash_insert(infra, e->hash, e, e->data, NULL); + else lock_rw_unlock(&e->lock); + return 1; +} diff --git a/services/cache/infra.h b/services/cache/infra.h index 486a041e3..027187034 100644 --- a/services/cache/infra.h +++ b/services/cache/infra.h @@ -70,8 +70,6 @@ struct infra_host_data { struct lruhash* lameness; /** edns version that the host supports, -1 means no EDNS */ int edns_version; - /** edns message size that the host advertizes, 512 by default. */ - uint16_t edns_size; }; /** @@ -101,6 +99,12 @@ struct infra_lame_data { #define HOST_LAME_TTL 900 /** default size of the host cache, number of entries */ #define HOST_DEFAULT_SIZE 1000 +/** infra host cache default hash lookup size */ +#define INFRA_HOST_STARTSIZE 32 +/** infra lame cache default hash lookup size */ +#define INFRA_LAME_STARTSIZE 2 +/** infra lame cache max memory per host, for this many entries */ +#define INFRA_LAME_MAXMEM 1000 /** * Create infra cache. @@ -122,15 +126,16 @@ void infra_delete(struct slabhash* infra); * @param addrlen: length of addr. * @param wr: set to true to get a writelock on the entry. * @param timenow: what time it is now. + * @param key: the key for the host, returned so caller can unlock when done. * @return: host data or NULL if not found or expired. */ -struct infra_data* infra_lookup_host(struct slabhash* infra, +struct infra_host_data* infra_lookup_host(struct slabhash* infra, struct sockaddr_storage* addr, socklen_t addrlen, int wr, - time_t timenow); + time_t timenow, struct infra_host_key** key); /** * Find host information to send a packet. Creates new entry if not found. - * Lameness is empty. EDNS is 0, size is 512, and rtt is returned for + * Lameness is empty. EDNS is 0 (try with first), and rtt is returned for * the first message to it. * @param infra: infrastructure cache. * @param addr: host address. @@ -152,7 +157,7 @@ int infra_host(struct slabhash* infra, struct sockaddr_storage* addr, * @param timenow: what time it is now. * @return: 0 if not lame or unknown or timed out, true if lame. */ -int infra_lookup_lame(struct infra_data* host, +int infra_lookup_lame(struct infra_host_data* host, uint8_t* name, size_t namelen, time_t timenow); /** @@ -174,7 +179,8 @@ int infra_set_lame(struct slabhash* infra, * @param infra: infrastructure cache. * @param addr: host address. * @param addrlen: length of addr. - * @param roundtrip: estimate of roundtrip time or -1 for timeout. + * @param roundtrip: estimate of roundtrip time in milliseconds or -1 for + * timeout. * @param timenow: what time it is now. * @return: 0 on error. */ @@ -188,12 +194,11 @@ int infra_rtt_update(struct slabhash* infra, * @param addr: host address. * @param addrlen: length of addr. * @param edns_version: the version that it publishes. - * @param udp_size: what udp size it can handle. * @param timenow: what time it is now. * @return: 0 on error. */ int infra_edns_update(struct slabhash* infra, struct sockaddr_storage* addr, socklen_t addrlen, - int edns_version, uint16_t udp_size, time_t timenow); + int edns_version, time_t timenow); #endif /* SERVICES_CACHE_INFRA_H */ diff --git a/util/net_help.c b/util/net_help.c index 88aecd379..3944dc0b7 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -109,3 +109,15 @@ write_iov_buffer(ldns_buffer* buffer, struct iovec* iov, size_t iovlen) } ldns_buffer_flip(buffer); } + +void* +memdup(void* data, size_t len) +{ + void* d; + if(!data) return NULL; + if(len == 0) return NULL; + d = malloc(len); + if(!d) return NULL; + memcpy(d, data, len); + return d; +} diff --git a/util/net_help.h b/util/net_help.h index e18a24afd..a859e312e 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -100,4 +100,12 @@ int is_pow2(size_t num); */ void write_iov_buffer(ldns_buffer* buffer, struct iovec* iov, size_t iovlen); +/** + * Allocate memory and copy over contents. + * @param data: what to copy over. + * @param len: length of data. + * @return: NULL on malloc failure, or newly malloced data. + */ +void* memdup(void* data, size_t len); + #endif /* NET_HELP_H */