diff mbox series

[libgpiod,1/2] core: keep memory usage of struct line_config in check

Message ID 20250424-line_config_mem_use-v1-1-fa0abdcf0cdf@linaro.org
State New
Headers show
Series [libgpiod,1/2] core: keep memory usage of struct line_config in check | expand

Commit Message

Bartosz Golaszewski April 24, 2025, 12:06 p.m. UTC
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

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: b7ba732e6a93 ("treewide: libgpiod v2 implementation")
Closes: https://github.com/brgl/libgpiod/issues/130
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 lib/line-config.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/lib/line-config.c b/lib/line-config.c
index 9302c1b..24357f3 100644
--- a/lib/line-config.c
+++ b/lib/line-config.c
@@ -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;