mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-10-26 12:24:42 +00:00
Fix severe SIGHUP reload slowdown caused by store_name quadratic search
When reloading large hosts files (e.g. 1.7M addn-hosts entries) on SIGHUP, free_names() previously only reset block->last = 0 without unlinking or moving blocks to a spare pool. As a result, all existing blocks remained in hostblocks. When reloading, the head block was filled first and stayed at the head while full, forcing store_name() to linearly traverse all previously filled blocks for every subsequent name insertion. This resulted in O(N^2) complexity, taking ~10 minutes for 1.7M records. Fix this by introducing nameblock_spare (matching config_spare / big_free spare pool semantics), moving freed blocks to the spare pool in free_names(), and restricting store_name() to only check the active head block before popping from nameblock_spare (or mallocing). This restores O(1) per-entry insertion and reduces reload time from 10 minutes to ~3 seconds.
This commit is contained in:
1 parent
f031f5b4b3
commit
a9cb3971c6
1 file changed
+32
-14
+32
-14
@@ -217,6 +217,8 @@ static void free_config_crec(struct crec *p)
|
||||
config_spare = p;
|
||||
}
|
||||
|
||||
static struct nameblock *nameblock_spare = NULL;
|
||||
|
||||
static char *store_name(unsigned int namelen, unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
@@ -225,21 +227,28 @@ static char *store_name(unsigned int namelen, unsigned int index)
|
||||
if (namelen > NAMEBLOCK_CHARS)
|
||||
return NULL;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
break;
|
||||
if (hostblocks && hostblocks->index == index && NAMEBLOCK_CHARS - hostblocks->last >= namelen)
|
||||
{
|
||||
ret = &hostblocks->data[hostblocks->last];
|
||||
hostblocks->last += namelen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!block && ((block = whine_malloc(sizeof(struct nameblock)))))
|
||||
if (nameblock_spare)
|
||||
{
|
||||
block = nameblock_spare;
|
||||
nameblock_spare = nameblock_spare->next;
|
||||
}
|
||||
else
|
||||
block = whine_malloc(sizeof(struct nameblock));
|
||||
|
||||
if (block)
|
||||
{
|
||||
block->next = hostblocks;
|
||||
block->index = index;
|
||||
block->last = namelen;
|
||||
hostblocks = block;
|
||||
}
|
||||
|
||||
if (block && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
{
|
||||
ret = &block->data[block->last];
|
||||
block->last += namelen;
|
||||
ret = &block->data[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -247,11 +256,20 @@ static char *store_name(unsigned int namelen, unsigned int index)
|
||||
|
||||
static void free_names(unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
struct nameblock *block, *tmp, **up;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (index == UID_NONE || block->index == index)
|
||||
block->last = 0;
|
||||
for (up = &hostblocks, block = hostblocks; block; block = tmp)
|
||||
{
|
||||
tmp = block->next;
|
||||
if (index == UID_NONE || block->index == index)
|
||||
{
|
||||
*up = tmp;
|
||||
block->next = nameblock_spare;
|
||||
nameblock_spare = block;
|
||||
}
|
||||
else
|
||||
up = &block->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
|
||||
|
||||
Reference in new issue
Block a user