From: W.C.A. Wijngaards Date: Thu, 14 Aug 2025 10:20:10 +0000 (+0200) Subject: - Fix to decouple file descriptor activity and cache lookups in X-Git-Tag: release-1.24.0rc1~29 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=991108af8d73d650d79ffebe33eac840e8bd8ad2;p=thirdparty%2Funbound.git - Fix to decouple file descriptor activity and cache lookups in dump_cache. --- diff --git a/daemon/cachedump.c b/daemon/cachedump.c index daa4e0ab1..941cb9dfc 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -62,38 +62,81 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" +static void spool_txt_printf(struct config_strlist_head* txt, + const char* format, ...) ATTR_FORMAT(printf, 2, 3); + +/** Append to strlist at end, and log error if out of memory. */ +static void +spool_txt_string(struct config_strlist_head* txt, char* str) +{ + if(!cfg_strlist_append(txt, strdup(str))) { + log_err("out of memory in spool text"); + } +} + +/** Spool txt to spool list. */ +static void +spool_txt_vmsg(struct config_strlist_head* txt, const char* format, + va_list args) +{ + char msg[65535]; + vsnprintf(msg, sizeof(msg), format, args); + spool_txt_string(txt, msg); +} + +/** Print item to spool list. On alloc failure the list is as before. */ +static void +spool_txt_printf(struct config_strlist_head* txt, const char* format, ...) +{ + va_list args; + va_start(args, format); + spool_txt_vmsg(txt, format, args); + va_end(args); +} + /** dump one rrset zonefile line */ -static int -dump_rrset_line(RES* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i) +static void +dump_rrset_line(struct config_strlist_head* txt, struct ub_packed_rrset_key* k, + time_t now, size_t i) { char s[65535]; if(!packed_rr_to_string(k, i, now, s, sizeof(s))) { - return ssl_printf(ssl, "BADRR\n"); + spool_txt_string(txt, "BADRR\n"); + return; } - return ssl_printf(ssl, "%s", s); + spool_txt_string(txt, s); } /** dump rrset key and data info */ -static int -dump_rrset(RES* ssl, struct ub_packed_rrset_key* k, +static void +dump_rrset(struct config_strlist_head* txt, struct ub_packed_rrset_key* k, struct packed_rrset_data* d, time_t now) { size_t i; /* rd lock held by caller */ - if(!k || !d) return 1; - if(k->id == 0) return 1; /* deleted */ - if(d->ttl < now) return 1; /* expired */ + if(!k || !d) return; + if(k->id == 0) return; /* deleted */ + if(d->ttl < now) return; /* expired */ /* meta line */ - if(!ssl_printf(ssl, ";rrset%s " ARG_LL "d %u %u %d %d\n", + spool_txt_printf(txt, ";rrset%s " ARG_LL "d %u %u %d %d\n", (k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"", (long long)(d->ttl - now), (unsigned)d->count, (unsigned)d->rrsig_count, (int)d->trust, (int)d->security - )) - return 0; + ); for(i=0; icount + d->rrsig_count; i++) { - if(!dump_rrset_line(ssl, k, now, i)) + dump_rrset_line(txt, k, now, i); + } +} + +/** Spool strlist to the output. */ +static int +spool_strlist(RES* ssl, struct config_strlist* list) +{ + struct config_strlist* s; + for(s=list; s; s=s->next) { + if(!ssl_printf(ssl, "%s", s->str)) return 0; } return 1; @@ -101,13 +144,17 @@ dump_rrset(RES* ssl, struct ub_packed_rrset_key* k, /** dump lruhash cache and call callback for every item. */ static int -dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg) +dump_lruhash(struct lruhash* table, + void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*), + RES* ssl, void* arg) { int just_started = 1; int not_done = 1; hashvalue_type hash; size_t num = 0; /* number of entries processed. */ size_t max = 2; /* number of entries after which it unlocks. */ + struct config_strlist_head txt; /* Text strings spooled. */ + memset(&txt, 0, sizeof(txt)); while(not_done) { size_t i; /* hash bin. */ @@ -128,11 +175,7 @@ dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg) lock_quick_lock(&bin->lock); for(e = bin->overflow_list; e; e = e->overflow_next) { /* Entry e is locked by the func. */ - if(!(*func)(e, arg)) { - lock_quick_unlock(&bin->lock); - lock_quick_unlock(&table->lock); - return 0; - } + func(e, &txt, arg); num_bin++; } lock_quick_unlock(&bin->lock); @@ -165,13 +208,34 @@ dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg) } } lock_quick_unlock(&table->lock); + /* Print the spooled items, that are collected while the + * locks are locked. The print happens while they are not + * locked. */ + if(txt.first) { + if(!spool_strlist(ssl, txt.first)) { + config_delstrlist(txt.first); + return 0; + } + config_delstrlist(txt.first); + memset(&txt, 0, sizeof(txt)); + } + } + /* Print the final spooled items. */ + if(txt.first) { + if(!spool_strlist(ssl, txt.first)) { + config_delstrlist(txt.first); + return 0; + } + config_delstrlist(txt.first); } return 1; } /** dump slabhash cache and call callback for every item. */ static int -dump_slabhash(struct slabhash* sh, int (*func)(void*, void*), void* arg) +dump_slabhash(struct slabhash* sh, + void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*), + RES* ssl, void* arg) { /* Process a number of items at a time, then unlock the cache, * so that ordinary processing can continue. Keep an iteration marker @@ -180,7 +244,7 @@ dump_slabhash(struct slabhash* sh, int (*func)(void*, void*), void* arg) * can grow. */ size_t slab; for(slab=0; slabsize; slab++) { - if(!dump_lruhash(sh->array[slab], func, arg)) + if(!dump_lruhash(sh->array[slab], func, ssl, arg)) return 0; } return 1; @@ -195,20 +259,16 @@ struct dump_info { }; /** Dump the rrset cache entry */ -static int -dump_rrset_entry(void* entryp, void* arg) +static void +dump_rrset_entry(struct lruhash_entry* e, struct config_strlist_head* txt, + void* arg) { struct dump_info* dump_info = (struct dump_info*)arg; - struct lruhash_entry* e = (struct lruhash_entry*)entryp; lock_rw_rdlock(&e->lock); - if(!dump_rrset(dump_info->ssl, (struct ub_packed_rrset_key*)e->key, + dump_rrset(txt, (struct ub_packed_rrset_key*)e->key, (struct packed_rrset_data*)e->data, - *dump_info->worker->env.now)) { - lock_rw_unlock(&e->lock); - return 0; - } + *dump_info->worker->env.now); lock_rw_unlock(&e->lock); - return 1; } /** dump rrset cache */ @@ -220,14 +280,14 @@ dump_rrset_cache(RES* ssl, struct worker* worker) dump_info.worker = worker; dump_info.ssl = ssl; if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0; - if(!dump_slabhash(&r->table, &dump_rrset_entry, &dump_info)) + if(!dump_slabhash(&r->table, &dump_rrset_entry, ssl, &dump_info)) return 0; return ssl_printf(ssl, "END_RRSET_CACHE\n"); } /** dump message to rrset reference */ -static int -dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k) +static void +dump_msg_ref(struct config_strlist_head* txt, struct ub_packed_rrset_key* k) { char* nm, *tp, *cl; nm = sldns_wire2str_dname(k->rk.dname, k->rk.dname_len); @@ -237,30 +297,25 @@ dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k) free(nm); free(tp); free(cl); - return ssl_printf(ssl, "BADREF\n"); - } - if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) { - free(nm); - free(tp); - free(cl); - return 0; + spool_txt_string(txt, "BADREF\n"); + return; } + spool_txt_printf(txt, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags); free(nm); free(tp); free(cl); - - return 1; } /** dump message entry */ -static int -dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now) +static void +dump_msg(struct config_strlist_head* txt, struct query_info* k, + struct reply_info* d, time_t now) { size_t i; char* nm, *tp, *cl; - if(!k || !d) return 1; - if(d->ttl < now) return 1; /* expired */ - + if(!k || !d) return; + if(d->ttl < now) return; /* expired */ + nm = sldns_wire2str_dname(k->qname, k->qname_len); tp = sldns_wire2str_type(k->qtype); cl = sldns_wire2str_class(k->qclass); @@ -268,45 +323,35 @@ dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now) free(nm); free(tp); free(cl); - return 1; /* skip this entry */ + return; /* skip this entry */ } if(!rrset_array_lock(d->ref, d->rrset_count, now)) { /* rrsets have timed out or do not exist */ free(nm); free(tp); free(cl); - return 1; /* skip this entry */ + return; /* skip this entry */ } - + /* meta line */ - if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n", - nm, cl, tp, - (int)d->flags, (int)d->qdcount, - (long long)(d->ttl-now), (int)d->security, - (unsigned)d->an_numrrsets, - (unsigned)d->ns_numrrsets, - (unsigned)d->ar_numrrsets, - (int)d->reason_bogus, - d->reason_bogus_str?d->reason_bogus_str:"")) { - free(nm); - free(tp); - free(cl); - rrset_array_unlock(d->ref, d->rrset_count); - return 0; - } + spool_txt_printf(txt, + "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n", + nm, cl, tp, + (int)d->flags, (int)d->qdcount, + (long long)(d->ttl-now), (int)d->security, + (unsigned)d->an_numrrsets, + (unsigned)d->ns_numrrsets, + (unsigned)d->ar_numrrsets, + (int)d->reason_bogus, + d->reason_bogus_str?d->reason_bogus_str:""); free(nm); free(tp); free(cl); for(i=0; irrset_count; i++) { - if(!dump_msg_ref(ssl, d->rrsets[i])) { - rrset_array_unlock(d->ref, d->rrset_count); - return 0; - } + dump_msg_ref(txt, d->rrsets[i]); } rrset_array_unlock(d->ref, d->rrset_count); - - return 1; } /** copy msg to worker pad */ @@ -336,11 +381,11 @@ copy_msg(struct regional* region, struct lruhash_entry* e, } /** Dump the msg entry. */ -static int -dump_msg_entry(void* entryp, void* arg) +static void +dump_msg_entry(struct lruhash_entry* e, struct config_strlist_head* txt, + void* arg) { struct dump_info* dump_info = (struct dump_info*)arg; - struct lruhash_entry* e = (struct lruhash_entry*)entryp; struct query_info* k; struct reply_info* d; @@ -349,15 +394,13 @@ dump_msg_entry(void* entryp, void* arg) lock_rw_rdlock(&e->lock); if(!copy_msg(dump_info->worker->scratchpad, e, &k, &d)) { lock_rw_unlock(&e->lock); - return 0; + log_err("out of memory in dump_msg_entry"); + return; } lock_rw_unlock(&e->lock); /* Release lock so we can lookup the rrset references * in the rrset cache. */ - if(!dump_msg(dump_info->ssl, k, d, *dump_info->worker->env.now)) { - return 0; - } - return 1; + dump_msg(txt, k, d, *dump_info->worker->env.now); } /** dump msg cache */ @@ -368,7 +411,8 @@ dump_msg_cache(RES* ssl, struct worker* worker) dump_info.worker = worker; dump_info.ssl = ssl; if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0; - if(!dump_slabhash(worker->env.msg_cache, &dump_msg_entry, &dump_info)) + if(!dump_slabhash(worker->env.msg_cache, &dump_msg_entry, ssl, + &dump_info)) return 0; return ssl_printf(ssl, "END_MSG_CACHE\n"); } @@ -891,12 +935,18 @@ print_dp_main(RES* ssl, struct delegpt* dp, struct dns_msg* msg) struct ub_packed_rrset_key* k = msg->rep->rrsets[i]; struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; + struct config_strlist_head txt; + memset(&txt, 0, sizeof(txt)); if(d->security == sec_status_bogus) { if(!ssl_printf(ssl, "Address is BOGUS:\n")) return; } - if(!dump_rrset(ssl, k, d, 0)) + dump_rrset(&txt, k, d, 0); + if(!spool_strlist(ssl, txt.first)) { + config_delstrlist(txt.first); return; + } + config_delstrlist(txt.first); } delegpt_count_ns(dp, &n_ns, &n_miss); delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);