platform: limit the maximum size of sysctl cache
When the logging level is DEBUG or TRACE, we keep all the sysctl values we read in a cache to log how they change. Currently there is no limit on the size of this cache and it can take a large amount of memory. Implement a LRU cache where the oldest entries are deleted to make space for new ones. https://github.com/NetworkManager/NetworkManager/pull/294
This commit is contained in:
@@ -376,8 +376,8 @@ typedef struct {
|
|||||||
|
|
||||||
bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
|
bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM];
|
||||||
|
|
||||||
bool sysctl_get_warned;
|
|
||||||
GHashTable *sysctl_get_prev_values;
|
GHashTable *sysctl_get_prev_values;
|
||||||
|
CList sysctl_list;
|
||||||
|
|
||||||
NMUdevClient *udev_client;
|
NMUdevClient *udev_client;
|
||||||
|
|
||||||
@@ -4156,41 +4156,72 @@ _nm_logging_clear_platform_logging_cache (void)
|
|||||||
|
|
||||||
g_hash_table_destroy (priv->sysctl_get_prev_values);
|
g_hash_table_destroy (priv->sysctl_get_prev_values);
|
||||||
priv->sysctl_get_prev_values = NULL;
|
priv->sysctl_get_prev_values = NULL;
|
||||||
priv->sysctl_get_warned = FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *path;
|
||||||
|
CList lst;
|
||||||
|
char *value;
|
||||||
|
char path_data[];
|
||||||
|
} SysctlCacheEntry;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysctl_cache_entry_free (SysctlCacheEntry *entry)
|
||||||
|
{
|
||||||
|
c_list_unlink_stale (&entry->lst);
|
||||||
|
g_free (entry->value);
|
||||||
|
g_free (entry);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *contents)
|
_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *contents)
|
||||||
{
|
{
|
||||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
||||||
const char *prev_value = NULL;
|
SysctlCacheEntry *entry = NULL;
|
||||||
|
|
||||||
if (!priv->sysctl_get_prev_values) {
|
if (!priv->sysctl_get_prev_values) {
|
||||||
sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform);
|
sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform);
|
||||||
priv->sysctl_get_prev_values = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
|
c_list_init (&priv->sysctl_list);
|
||||||
|
priv->sysctl_get_prev_values = g_hash_table_new_full (nm_pstr_hash,
|
||||||
|
nm_pstr_equal,
|
||||||
|
(GDestroyNotify) sysctl_cache_entry_free,
|
||||||
|
NULL);
|
||||||
} else
|
} else
|
||||||
prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid);
|
entry = g_hash_table_lookup (priv->sysctl_get_prev_values, &pathid);
|
||||||
|
|
||||||
if (prev_value) {
|
if (entry) {
|
||||||
if (strcmp (prev_value, contents) != 0) {
|
if (!nm_streq (entry->value, contents)) {
|
||||||
gs_free char *contents_escaped = g_strescape (contents, NULL);
|
gs_free char *contents_escaped = g_strescape (contents, NULL);
|
||||||
gs_free char *prev_value_escaped = g_strescape (prev_value, NULL);
|
gs_free char *prev_value_escaped = g_strescape (entry->value, NULL);
|
||||||
|
|
||||||
_LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, contents_escaped, prev_value_escaped);
|
_LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, contents_escaped, prev_value_escaped);
|
||||||
g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
|
g_free (entry->value);
|
||||||
|
entry->value = g_strdup (contents);
|
||||||
}
|
}
|
||||||
|
c_list_unlink_stale (&entry->lst);
|
||||||
|
c_list_link_front (&priv->sysctl_list, &entry->lst);
|
||||||
} else {
|
} else {
|
||||||
gs_free char *contents_escaped = g_strescape (contents, NULL);
|
gs_free char *contents_escaped = g_strescape (contents, NULL);
|
||||||
|
SysctlCacheEntry *old;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen (pathid);
|
||||||
|
entry = g_malloc (sizeof (SysctlCacheEntry) + len + 1);
|
||||||
|
entry->value = g_strdup (contents);
|
||||||
|
entry->path = entry->path_data;
|
||||||
|
memcpy (entry->path_data, pathid, len + 1);
|
||||||
|
|
||||||
|
/* Remove oldest entry when the cache becomes too big */
|
||||||
|
if (g_hash_table_size (priv->sysctl_get_prev_values) > 1000) {
|
||||||
|
old = c_list_last_entry (&priv->sysctl_list, SysctlCacheEntry, lst);
|
||||||
|
g_hash_table_remove (priv->sysctl_get_prev_values, old);
|
||||||
|
}
|
||||||
|
|
||||||
_LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped);
|
_LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped);
|
||||||
g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
|
|
||||||
|
|
||||||
if ( !priv->sysctl_get_warned
|
g_hash_table_add (priv->sysctl_get_prev_values, entry);
|
||||||
&& g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
|
c_list_link_front (&priv->sysctl_list, &entry->lst);
|
||||||
_LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
|
|
||||||
priv->sysctl_get_warned = TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user