From: Marek VavruĊĦa Date: Sat, 12 May 2018 04:06:21 +0000 (-0700) Subject: use LRU for packet cache instead of persistent cache X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fmarek%2Flru-cache-packets;p=thirdparty%2Fknot-resolver.git use LRU for packet cache instead of persistent cache The primary motivation is that packet cache is shared with records, but it isn't useful for improving performance of popular domain lookups. Negative answers (with DNSSEC) are typically around 1kB, so it doesn't take many queries to force a full cache flush, including useful record data. This changes the packet cache to use a fixed-size LRU, which is more resilient to random prefix attacks, but it isn't persistent or shared between processes. --- diff --git a/lib/cache/api.c b/lib/cache/api.c index 63e205740..c9645379e 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -129,6 +129,11 @@ int kr_cache_open(struct kr_cache *cache, const struct kr_cdb_api *api, struct k /* Check cache ABI version */ kr_cache_make_checkpoint(cache); (void) assert_right_version(cache); + /* Create LRU cache for packets */ + cache->pkt_cache = lru_create_impl(KR_CACHE_DEFAULT_LRU_SIZE, mm, NULL); + if (cache->pkt_cache == NULL) { + return kr_error(ENOMEM); + } return 0; } @@ -140,6 +145,8 @@ void kr_cache_close(struct kr_cache *cache) if (cache_isvalid(cache)) { cache_op(cache, close); cache->db = NULL; + lru_free_impl(cache->pkt_cache); + cache->pkt_cache = NULL; } } @@ -379,11 +386,16 @@ static int cache_peek_real(kr_layer_t *ctx, knot_pkt_t *pkt) const uint8_t lowest_rank = get_lowest_rank(req, qry); /** 1. find the name or the closest (available) zone, not considering wildcards - * 1a. exact name+type match (can be negative answer in insecure zones) + * 1a. exact name+type match in packet cache (LRU) + * 1b. exact name+type match (can be negative answer in insecure zones) */ knot_db_val_t key = key_exact_type_maypkt(k, qry->stype); knot_db_val_t val = { NULL, 0 }; - ret = cache_op(cache, read, &key, &val, 1); + ret = find_from_pkt_cache(req, &key, &val, lowest_rank); + if (ret != 0) { + ret = cache_op(cache, read, &key, &val, 1); + } + if (!ret) { /* found an entry: test conditions, materialize into pkt, etc. */ ret = found_exact_hit(ctx, pkt, val, lowest_rank); diff --git a/lib/cache/api.h b/lib/cache/api.h index 35ac4ba75..531976f0b 100644 --- a/lib/cache/api.h +++ b/lib/cache/api.h @@ -19,8 +19,10 @@ #include #include #include + #include "lib/cache/cdb_api.h" #include "lib/defines.h" +#include "lib/generic/lru.h" #include "contrib/ucw/config.h" /*uint*/ /** When knot_pkt is passed from cache without ->wire, this is the ->size. */ @@ -52,6 +54,8 @@ struct kr_cache /* A pair of stamps for detection of real-time shifts during runtime. */ struct timeval checkpoint_walltime; /**< Wall time on the last check-point. */ uint64_t checkpoint_monotime; /**< Monotonic milliseconds on the last check-point. */ + + struct lru *pkt_cache; /**< LRU cache for packets */ }; /** diff --git a/lib/cache/entry_pkt.c b/lib/cache/entry_pkt.c index 972bae62f..0ae6bc64d 100644 --- a/lib/cache/entry_pkt.c +++ b/lib/cache/entry_pkt.c @@ -139,17 +139,20 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry, /* For now we stash the full packet byte-exactly as it came from upstream. */ const uint16_t pkt_size = pkt->size; - knot_db_val_t val_new_entry = { - .data = NULL, - .len = offsetof(struct entry_h, data) + sizeof(pkt_size) + pkt->size, - }; + const size_t entry_len = offsetof(struct entry_h, data) + sizeof(pkt_size) + pkt_size; + /* Prepare raw memory for the new entry and fill it. */ struct kr_cache *cache = &req->ctx->cache; - ret = entry_h_splice(&val_new_entry, rank, key, k->type, pkt_type, - owner, qry, cache, qry->timestamp.tv_sec); - if (ret) return; /* some aren't really errors */ - assert(val_new_entry.data); - struct entry_h *eh = val_new_entry.data; + if (cache->pkt_cache == NULL) { + return; + } + + struct entry_h *eh = lru_get_impl(cache->pkt_cache, key.data, (uint)key.len, (uint)entry_len, true, NULL); + if (eh == NULL) { + return; + } + + memset(eh, 0, offsetof(struct entry_h, data)); eh->time = qry->timestamp.tv_sec; eh->ttl = MAX(MIN(packet_ttl(pkt, is_negative), cache->ttl_max), cache->ttl_min); eh->rank = rank; @@ -162,10 +165,38 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry, auto_free char *type_str = kr_rrtype_text(pkt_type), *owner_str = kr_dname_text(owner); VERBOSE_MSG(qry, "=> stashed packet: rank 0%.2o, TTL %d, " - "%s %s (%d B)\n", + "%s %s (%zu B)\n", eh->rank, eh->ttl, - type_str, owner_str, (int)val_new_entry.len); + type_str, owner_str, entry_len); + } +} + +int find_from_pkt_cache(struct kr_request *req, knot_db_val_t *key, knot_db_val_t *val, uint8_t lowest_rank) +{ + struct kr_query *qry = req->current_query; + struct kr_cache *cache = &req->ctx->cache; + if (cache->pkt_cache == NULL) { + return kr_error(EINVAL); + } + + /* Only check if key-value exists in LRU, don't insert */ + struct entry_h *eh = lru_get_impl(cache->pkt_cache, key->data, (uint)key->len, 0, false, NULL); + if (eh == NULL) { + return kr_error(ENOENT); + } + + int32_t new_ttl = get_new_ttl(eh, qry, qry->sname, qry->stype, qry->timestamp.tv_sec); + if (new_ttl < 0 || eh->rank < lowest_rank) { + return kr_error(ENOENT); } + + /* Calculate value size */ + uint16_t pkt_len; + memcpy(&pkt_len, eh->data, sizeof(pkt_len)); + + val->data = eh; + val->len = offsetof(struct entry_h, data) + sizeof(pkt_len) + pkt_len; + return kr_ok(); } diff --git a/lib/cache/impl.h b/lib/cache/impl.h index 9b0003430..ca19c18d9 100644 --- a/lib/cache/impl.h +++ b/lib/cache/impl.h @@ -137,6 +137,9 @@ void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry, int answer_from_pkt(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type, const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl); +/** Try to find answer from packet cache. + */ +int find_from_pkt_cache(struct kr_request *req, knot_db_val_t *key, knot_db_val_t *val, uint8_t lowest_rank); /** Record is expiring if it has less than 1% TTL (or less than 5s) */ static inline bool is_expiring(uint32_t orig_ttl, uint32_t new_ttl) diff --git a/lib/defines.h b/lib/defines.h index 659558837..c7e659afb 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -74,6 +74,9 @@ static inline int KR_COLD kr_error(int x) { #define KR_EDNS_PAYLOAD 4096 /* Default UDP payload (max unfragmented UDP is 1452B) */ #define KR_CACHE_DEFAULT_TTL_MIN (5) /* avoid bursts of queries */ #define KR_CACHE_DEFAULT_TTL_MAX (6 * 24 * 3600) /* 6 days, like the root NS TTL */ +#ifndef KR_CACHE_DEFAULT_LRU_SIZE +#define KR_CACHE_DEFAULT_LRU_SIZE 65536 /**< LRU packet cache size. */ +#endif #define KR_DNAME_STR_MAXLEN (KNOT_DNAME_TXT_MAXLEN + 1) #define KR_RRTYPE_STR_MAXLEN (16 + 1)