From: Sascha Hauer <s.hauer@pengutronix.de>
To: BAREBOX <barebox@lists.infradead.org>
Subject: [PATCH 01/10] globalvar: add scopes to snapshot and restore globalvars
Date: Fri, 30 Oct 2026 11:53:59 +0100 [thread overview]
Message-ID: <20261030-boot-globalvar-stack-v1-1-a03803e1100c@pengutronix.de> (raw)
In-Reply-To: <20261030-boot-globalvar-stack-v1-0-a03803e1100c@pengutronix.de>
Code that runs foreign code, like boot scripts, often wants to revert
whatever that code changed in the global namespace afterwards. So far
this is done by hand for each use case: either by saving and restoring a
fixed list of variables, or by putting variables into a dedicated
namespace which is cleared afterwards.
Add globalvar_scope_push() and globalvar_scope_pop() to do this
generically. A scope records the current values of all globalvars whose
name begins with one of the given prefixes. Popping it restores changed
values, removes variables created in the meantime and creates removed
variables again. Variables are only removed or recreated if they were
created with globalvar_add_simple(), which is what the shell's global
command and most dynamically created variables use. For all other
globalvars, which are backed by a C variable, only the value is
restored, through the regular setter so the C variable follows.
Scopes can be nested as long as they are popped in reverse order.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/globalvar.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/globalvar.h | 29 +++++++++++
2 files changed, 171 insertions(+)
diff --git a/common/globalvar.c b/common/globalvar.c
index 876379b253..4686378ded 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -456,6 +456,148 @@ void globalvar_set_match(const char *match, const char *val)
}
}
+struct globalvar_scope_var {
+ struct list_head list;
+ char *name;
+ char *value;
+ bool unqualified;
+ bool seen;
+};
+
+struct globalvar_scope {
+ const char * const *prefixes;
+ struct list_head vars;
+};
+
+static bool globalvar_scope_match(const struct globalvar_scope *scope,
+ const char *name)
+{
+ const char * const *prefix;
+
+ for (prefix = scope->prefixes; *prefix; prefix++)
+ if (str_has_prefix(name, *prefix))
+ return true;
+
+ return false;
+}
+
+static struct globalvar_scope_var *
+globalvar_scope_find(struct globalvar_scope *scope, const char *name)
+{
+ struct globalvar_scope_var *var;
+
+ list_for_each_entry(var, &scope->vars, list)
+ if (!strcmp(var->name, name))
+ return var;
+
+ return NULL;
+}
+
+/**
+ * __globalvar_scope_push - snapshot a set of globalvars
+ * @prefixes: NULL terminated array of name prefixes to snapshot, which
+ * must stay valid until globalvar_scope_pop()
+ *
+ * Records the current value of all globalvars whose name starts with one
+ * of @prefixes. A later globalvar_scope_pop() reverts all changes done
+ * to these variables in the meantime, including adding and removing them.
+ * Scopes can be nested, but must be popped in reverse order.
+ *
+ * Return: the scope to be passed to globalvar_scope_pop()
+ */
+struct globalvar_scope *__globalvar_scope_push(const char * const *prefixes)
+{
+ struct globalvar_scope *scope;
+ struct param_d *p;
+
+ scope = xzalloc(sizeof(*scope));
+ scope->prefixes = prefixes;
+ INIT_LIST_HEAD(&scope->vars);
+
+ dev_for_each_param(&global_device, p) {
+ struct globalvar_scope_var *var;
+
+ if (!globalvar_scope_match(scope, p->name))
+ continue;
+
+ var = xzalloc(sizeof(*var));
+ var->name = xstrdup(p->name);
+ var->value = xstrdup(p->get(p->bobj, p) ?: "");
+ var->unqualified = p->flags & PARAM_GLOBALVAR_UNQUALIFIED;
+ list_add_tail(&var->list, &scope->vars);
+ }
+
+ return scope;
+}
+
+/**
+ * globalvar_scope_pop - revert globalvars to a snapshot
+ * @scope: the scope returned by globalvar_scope_push()
+ *
+ * Restores all globalvars covered by @scope to the values they had when
+ * the scope was pushed: changed values are set back, variables created
+ * in the meantime are removed and removed variables are created again.
+ * Variables which were not created with globalvar_add_simple() are never
+ * removed or recreated, only their values are restored.
+ *
+ * @scope is freed afterwards.
+ */
+void globalvar_scope_pop(struct globalvar_scope *scope)
+{
+ struct globalvar_scope_var *var, *vtmp;
+ struct param_d *p, *tmp;
+ int ret;
+
+ if (!scope)
+ return;
+
+ dev_for_each_param_safe(&global_device, p, tmp) {
+ const char *cur;
+
+ if (!globalvar_scope_match(scope, p->name))
+ continue;
+
+ var = globalvar_scope_find(scope, p->name);
+ if (!var) {
+ if (!(p->flags & PARAM_GLOBALVAR_UNQUALIFIED))
+ continue;
+
+ pr_debug("scope: removing global.%s\n", p->name);
+ param_remove(p);
+ continue;
+ }
+
+ var->seen = true;
+
+ cur = p->get(p->bobj, p) ?: "";
+ if (!strcmp(cur, var->value))
+ continue;
+
+ pr_debug("scope: restoring global.%s: \"%s\" -> \"%s\"\n",
+ p->name, cur, var->value);
+
+ ret = dev_set_param(&global_device, p->name, var->value);
+ if (ret)
+ pr_warn("Cannot restore global.%s=\"%s\": %pe\n",
+ p->name, var->value, ERR_PTR(ret));
+ }
+
+ list_for_each_entry_safe(var, vtmp, &scope->vars, list) {
+ if (!var->seen && var->unqualified) {
+ pr_debug("scope: recreating global.%s=\"%s\"\n",
+ var->name, var->value);
+ globalvar_add_simple(var->name, var->value);
+ }
+
+ list_del(&var->list);
+ free(var->name);
+ free(var->value);
+ free(var);
+ }
+
+ free(scope);
+}
+
void globalvar_set(const char *name, const char *val)
{
dev_set_param(&global_device, name, val);
diff --git a/include/globalvar.h b/include/globalvar.h
index e369616c63..7fbfdd1284 100644
--- a/include/globalvar.h
+++ b/include/globalvar.h
@@ -10,6 +10,24 @@
extern struct device global_device;
+struct globalvar_scope;
+
+/**
+ * globalvar_scope_push - snapshot a set of globalvars
+ * @...: name prefixes of the globalvars to snapshot
+ *
+ * See __globalvar_scope_push(). The scope borrows the prefixes until
+ * globalvar_scope_pop(). Collecting them in a static array makes the
+ * compiler reject prefixes that don't live that long, as only string
+ * literals and addresses of static objects are allowed as initializers.
+ *
+ * Return: the scope to be passed to globalvar_scope_pop()
+ */
+#define globalvar_scope_push(...) ({ \
+ static const char * const __prefixes[] = { __VA_ARGS__, NULL }; \
+ __globalvar_scope_push(__prefixes); \
+})
+
#ifdef CONFIG_GLOBALVAR
static inline const char *globalvar_get(const char *name)
@@ -24,6 +42,9 @@ char *globalvar_get_match(const char *match, const char *separator);
void globalvar_set_match(const char *match, const char *val);
void globalvar_set(const char *name, const char *val);
+struct globalvar_scope *__globalvar_scope_push(const char * const *prefixes);
+void globalvar_scope_pop(struct globalvar_scope *scope);
+
int globalvar_add_simple_string(const char *name, char **value);
int globalvar_add_simple_int(const char *name, int *value,
const char *format);
@@ -126,6 +147,14 @@ static inline void globalvar_set_match(const char *match, const char *val) {}
static inline void globalvar_set(const char *name, const char *val) {}
+static inline struct globalvar_scope *
+__globalvar_scope_push(const char * const *prefixes)
+{
+ return NULL;
+}
+
+static inline void globalvar_scope_pop(struct globalvar_scope *scope) {}
+
static inline int nvvar_load(void)
{
return 0;
--
2.47.3
next prev parent reply other threads:[~2026-10-30 10:55 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 10:53 [PATCH 00/10] boot: revert boot related globalvars after booting Sascha Hauer
2026-10-30 10:53 ` Sascha Hauer [this message]
2026-10-30 10:54 ` [PATCH 02/10] bootm: revert globalvar changes in bootm_boot_cleanup() Sascha Hauer
2026-10-30 10:54 ` [PATCH 03/10] boot: revert globalvar changes after each boot entry Sascha Hauer
2026-10-30 10:54 ` [PATCH 04/10] bootchooser: use a globalvar scope for its bootargs Sascha Hauer
2026-10-30 10:54 ` [PATCH 05/10] test: py: add test for reverting globalvars after boot Sascha Hauer
2026-10-30 10:54 ` [PATCH 06/10] bootargs: remove unused linux_bootargs_overwrite() Sascha Hauer
2026-10-30 10:54 ` [PATCH 07/10] of: add watchdog bootarg directly instead of via a globalvar Sascha Hauer
2026-10-30 10:54 ` [PATCH 08/10] boot: drop the dyn infix from the bootentries bootargs variable Sascha Hauer
2026-10-30 10:54 ` [PATCH 09/10] defaultenv: don't use global.linux.bootargs.dyn.* in boot entries Sascha Hauer
2026-10-30 10:54 ` [PATCH 10/10] Documentation: migration-guides: describe reverting boot globalvars Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030-boot-globalvar-stack-v1-1-a03803e1100c@pengutronix.de \
--to=s.hauer@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox