dns: refactor update() in NMDnsSystemdResolved to use a hash table

Use a GHashTable instead of a GArray to construct the list of
@interfaces. Also, use NMCListElem instead of GList. With this,
the runtime is O(n*log(n)) instead of O(n^2).

I belive, we should take care that all our code has a reasonable
runtime complexity, even in common use-cases the number of elements
is small. This is not about performace, because likely we expect few
entries anyway, and the direct GArray implementation is likely faster
in those cases. It's about using the data structure that best suits the
access pattern.

The log(n) part comes from sorting the keys. I also believe we should
always aim for a stable behavior. When sending the D-Bus request to
resolved, the order of elements should be in ~some~ defined order.

(cherry picked from commit 4be4a3c21f)
This commit is contained in:
Thomas Haller 2017-12-19 15:07:53 +01:00 committed by Beniamino Galvani
parent f2590e86b3
commit 2642407dc2

View file

@ -31,6 +31,7 @@
#include <sys/stat.h>
#include <linux/if.h>
#include "nm-utils/nm-c-list.h"
#include "nm-core-internal.h"
#include "platform/nm-platform.h"
#include "nm-utils.h"
@ -49,7 +50,7 @@
typedef struct {
int ifindex;
GList *configs;
CList configs_lst_head;
} InterfaceConfig;
/*****************************************************************************/
@ -83,6 +84,13 @@ G_DEFINE_TYPE (NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN
/*****************************************************************************/
static void
_interface_config_free (InterfaceConfig *config)
{
nm_c_list_elem_free_all (&config->configs_lst_head, NULL);
g_slice_free (InterfaceConfig, config);
}
static void
call_done (GObject *source, GAsyncResult *r, gpointer user_data)
{
@ -101,42 +109,6 @@ call_done (GObject *source, GAsyncResult *r, gpointer user_data)
}
}
static void
add_interface_configuration (NMDnsSystemdResolved *self,
GArray *interfaces,
const NMDnsIPConfigData *data,
gboolean skip)
{
int i;
InterfaceConfig *ic = NULL;
int ifindex;
if (NM_IS_IP4_CONFIG (data->config))
ifindex = nm_ip4_config_get_ifindex (data->config);
else if (NM_IS_IP6_CONFIG (data->config))
ifindex = nm_ip6_config_get_ifindex (data->config);
else
g_return_if_reached ();
for (i = 0; i < interfaces->len; i++) {
InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i);
if (ifindex == tic->ifindex) {
ic = tic;
break;
}
}
if (!ic) {
g_array_set_size (interfaces, interfaces->len + 1);
ic = &g_array_index (interfaces, InterfaceConfig,
interfaces->len - 1);
ic->ifindex = ifindex;
}
if (!skip)
ic->configs = g_list_append (ic->configs, data->config);
}
static void
update_add_ip_config (NMDnsSystemdResolved *self,
GVariantBuilder *dns,
@ -229,7 +201,7 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
{
NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
GVariantBuilder dns, domains;
GList *l;
NMCListElem *elem;
g_variant_builder_init (&dns, G_VARIANT_TYPE ("(ia(iay))"));
g_variant_builder_add (&dns, "i", ic->ifindex);
@ -239,8 +211,8 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
g_variant_builder_add (&domains, "i", ic->ifindex);
g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)"));
for (l = ic->configs; l; l = l->next)
update_add_ip_config (self, &dns, &domains, l->data);
c_list_for_each_entry (elem, &ic->configs_lst_head, lst)
update_add_ip_config (self, &dns, &domains, elem->data);
g_variant_builder_close (&dns);
g_variant_builder_close (&domains);
@ -286,33 +258,59 @@ update (NMDnsPlugin *plugin,
const char *hostname)
{
NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin);
GArray *interfaces = g_array_new (TRUE, TRUE, sizeof (InterfaceConfig));
gs_unref_hashtable GHashTable *interfaces = NULL;
gs_free gpointer *interfaces_keys = NULL;
guint interfaces_len;
guint i;
int prio, first_prio = 0;
interfaces = g_hash_table_new_full (nm_direct_hash, NULL,
NULL, (GDestroyNotify) _interface_config_free);
for (i = 0; i < configs->len; i++) {
const NMDnsIPConfigData *data = configs->pdata[i];
gboolean skip = FALSE;
InterfaceConfig *ic = NULL;
int ifindex;
prio = nm_ip_config_get_dns_priority (data->config);
if (i == 0)
first_prio = prio;
else if (first_prio < 0 && first_prio != prio)
skip = TRUE;
add_interface_configuration (self, interfaces, data, skip);
ifindex = nm_ip_config_get_ifindex (data->config);
ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (ifindex));
if (!ic) {
ic = g_slice_new (InterfaceConfig);
ic->ifindex = ifindex;
c_list_init (&ic->configs_lst_head);
g_hash_table_insert (interfaces, GINT_TO_POINTER (ifindex), ic);
}
if (!skip) {
c_list_link_tail (&ic->configs_lst_head,
&nm_c_list_elem_new_stale (data->config)->lst);
}
}
free_pending_updates (self);
for (i = 0; i < interfaces->len; i++) {
InterfaceConfig *ic = &g_array_index (interfaces, InterfaceConfig, i);
interfaces_keys = g_hash_table_get_keys_as_array (interfaces, &interfaces_len);
if (interfaces_len > 1) {
g_qsort_with_data (interfaces_keys,
interfaces_len,
sizeof (gpointer),
nm_cmp_int2ptr_p_with_data,
NULL);
}
for (i = 0; i < interfaces_len; i++) {
InterfaceConfig *ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (interfaces_keys[i]));
prepare_one_interface (self, ic);
g_list_free (ic->configs);
}
g_array_free (interfaces, TRUE);
send_updates (self);
return TRUE;