Optimise readng large number --server options at start up.

When re-reading upstream servers from /etc/resolv.conf or other
sources that can change dnsmasq tries to avoid memory fragmentation by
re-using existing records that are being re-read unchanged. This
involves seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long start
times with thousands of --server options because the work needed is
O(n^2). Handle this case more intelligently.  Thanks to Ye Zhou for
spotting the problem and an initial patch.
This commit is contained in:
Simon Kelley
2022-11-21 16:40:53 +00:00
parent d6d7527c95
commit 881eaa4dbc
3 changed files with 49 additions and 21 deletions

View File

@@ -49,6 +49,16 @@ version 2.88
in the domain it attests to be returned as unvalidated, and not in the domain it attests to be returned as unvalidated, and not
as a validation error. as a validation error.
Optimise reading large numbers of --server options. When re-reading
upstream servers from /etc/resolv.conf or other sources that
can change dnsmasq tries to avoid memory fragmentation by re-using
existing records that are being re-read unchanged. This involves
seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long
start times with thousands of --server options because the work
needed is O(n^2). Handle this case more intelligently.
Thanks to Ye Zhou for spotting the problem and an initial patch.
version 2.87 version 2.87
Allow arbitrary prefix lengths in --rev-server and Allow arbitrary prefix lengths in --rev-server and

View File

@@ -546,11 +546,23 @@ static int order_qsort(const void *a, const void *b)
return rc; return rc;
} }
/* When loading large numbers of server=.... lines during startup,
there's no possibility that there will be server records that can be reused, but
searching a long list for each server added grows as O(n^2) and slows things down.
This flag is set only if is known there may be free server records that can be reused.
There's a call to mark_servers(0) in read_opts() to reset the flag before
main config read. */
static int maybe_free_servers = 0;
/* Must be called before add_update_server() to set daemon->servers_tail */ /* Must be called before add_update_server() to set daemon->servers_tail */
void mark_servers(int flag) void mark_servers(int flag)
{ {
struct server *serv, **up; struct server *serv, **up;
maybe_free_servers = !!flag;
daemon->servers_tail = NULL; daemon->servers_tail = NULL;
/* mark everything with argument flag */ /* mark everything with argument flag */
@@ -667,27 +679,30 @@ int add_update_server(int flags,
and move to the end of the list, for order. The entry found may already and move to the end of the list, for order. The entry found may already
be at the end. */ be at the end. */
struct server **up, *tmp; struct server **up, *tmp;
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
{
/* Need to move down? */
if (serv->next)
{
*up = serv->next;
daemon->servers_tail->next = serv;
daemon->servers_tail = serv;
serv->next = NULL;
}
break;
}
else
up = &serv->next;
}
serv = NULL;
if (maybe_free_servers)
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if ((serv->flags & SERV_MARK) &&
hostname_isequal(alloc_domain, serv->domain))
{
/* Need to move down? */
if (serv->next)
{
*up = serv->next;
daemon->servers_tail->next = serv;
daemon->servers_tail = serv;
serv->next = NULL;
}
break;
}
else
up = &serv->next;
}
if (serv) if (serv)
{ {
free(alloc_domain); free(alloc_domain);

View File

@@ -5753,7 +5753,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
#endif #endif
add_txt("servers.bind", NULL, TXT_STAT_SERVERS); add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
#endif #endif
/* See comment above make_servers(). Optimises server-read code. */
mark_servers(0);
while (1) while (1)
{ {
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG