1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00

core: keep memory usage of struct line_config in check

We keep a single instance of a struct gpiod_line_settings object shared
by all offsets using it. Everytime gpiod_line_config_add_line_settings()
is called, we append it to the per-line-config linked list and reference
it from every per-offset structure.

However we don't keep track of the offsets referencing a settings object
and don't remove it when there are no more offsets associated with it -
we only do it when the user frees or resets the line-config object.

Extend the settings_node structure: make it a doubly-linked list -
allowing to easily remove individual nodes - and add a reference count.
Keep track of how many offsets reference a line-settings object and when
that reference count drops to 0, remove it.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Closes: https://github.com/brgl/libgpiod/issues/130
Link: https://lore.kernel.org/r/20250424-line_config_mem_use-v1-1-fa0abdcf0cdf@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This commit is contained in:
Bartosz Golaszewski
2025-04-24 14:06:16 +02:00
parent d9b156ecc2
commit 2560a857ce

View File

@@ -13,8 +13,10 @@
#define LINES_MAX (GPIO_V2_LINES_MAX)
struct settings_node {
struct settings_node *prev;
struct settings_node *next;
struct gpiod_line_settings *settings;
unsigned int refcnt;
};
struct per_line_config {
@@ -93,7 +95,7 @@ GPIOD_API int gpiod_line_config_add_line_settings(
size_t num_offsets, struct gpiod_line_settings *settings)
{
struct per_line_config *per_line;
struct settings_node *node;
struct settings_node *node, *old;
size_t i;
assert(config);
@@ -121,14 +123,29 @@ GPIOD_API int gpiod_line_config_add_line_settings(
return -1;
}
node->refcnt = 0;
node->next = config->sref_list;
if (config->sref_list)
config->sref_list->prev = node;
node->prev = NULL;
config->sref_list = node;
for (i = 0; i < num_offsets; i++) {
per_line = find_config(config, offsets[i]);
node->refcnt++;
per_line->offset = offsets[i];
old = per_line->node;
per_line->node = node;
if (old && !(--old->refcnt)) {
if (old->prev)
old->prev->next = old->next;
if (old->next)
old->next->prev = old->prev;
free(old->settings);
free(old);
}
}
return 0;