[U-Boot] [RFC, PATCH v4 02/16] env: extend interfaces to import/export U-Boot environment per context
AKASHI Takahiro
takahiro.akashi at linaro.org
Fri Jul 19 08:15:57 UTC 2019
On Fri, Jul 19, 2019 at 09:38:11AM +0200, Wolfgang Denk wrote:
> Dear AKASHI Takahiro,
>
> In message <20190717082525.891-3-takahiro.akashi at linaro.org> you wrote:
> > The following interfaces are extended to accept an additional argument,
> > context:
> > env_import() -> env_import_ext()
> > env_export() -> env_export_ext()
> > env_load() -> env_load_ext()
> > env_save() -> env_save_ext()
>
> Please don't such renames, see previous comments.
I didn't rename them.
> > -int env_import(const char *buf, int check)
> > +int env_import_ext(const char *buf, enum env_context ctx, int check)
env_import() is re-defined using env_import_ext() below.
> I think this needs at least additional documentation.
>
> From the explantions so far, a context is always associated with a
> variable, i. e. it is a property of the variable. Here it becomes
> clear that the context has an additional meaning, separate storage.
I described in my cover letter.
> It should be documented that one block of variables such as handled
> by the env import / export routines will always only process
> veriables of a specific context.
Okay, but this is not specific to this function.
I'm going to add detailed function descriptions
if you agree with these new interfaces. Do you?
>
> > - if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
> > - 0, NULL)) {
> > + if (himport_ext(&env_htab, ctx, (char *)ep->data, ep->data_size,
> > + /*
> > + * FIXME:
> > + * H_NOCLEAR is necessary here to handle
> > + * multiple contexts simultaneously.
> > + * This, however, breaks backward compatibility.
> > + */
> > + '\0', H_NOCLEAR, 0, 0, NULL)) {
>
> This is the result of a deign bug. You can never have "multiple
> contexts simultaneously", because the code as you designed it always
> has only one context per data block - and I can;t see how this could
> be changed, as the external storage format ('name=value\0') does not
> provide a way to include variable-specific context information.
Please read path#14 where a fat driver is modified to maintain UEFI
variables in U-Boot environment hash tables.
If UEFI variables need not be maintained in a single U-Boot environment,
my whole patch set goes in vain. (This is my "Plan B" implementation.)
> > - set_default_env("import failed", 0);
> > + if (ctx == ENVCTX_UBOOT)
> > + set_default_env("import failed", 0);
>
> Should we not also have default settings for other contexts as well?
Handling for default (initial) variables may vary context to context.
It should be implemented depending on their requirements.
> > +int env_import_redund_ext(const char *buf1, int buf1_read_fail,
> > + const char *buf2, int buf2_read_fail,
> > + enum env_context ctx)
> > +{
> > + /* TODO */
> > + return 0;
>
> !!
>
> Do you plan to implement redundant storage for UEFI data as well? It
> would make sense, wouldn't it?
Yes and no.
My current focus is to determine if my approach is acceptable or not.
> > - pr_err("Cannot export environment: errno = %d\n", errno);
> > + pr_err("Cannot export environment: errno = %d\n",
> > + errno);
>
> Why did you change this?
Maybe a mistake?
> > - env_out->crc = crc32(0, env_out->data, ENV_SIZE);
> > + if (*env_out) {
> > + envp = *env_out;
> > + } else {
> > + envp = malloc(sizeof(*envp) + len);
> > + if (!envp) {
> > + pr_err("Out of memory\n");
> > + free(data);
> > + return 1;
> > + }
> > + *env_out = envp;
> > +
> > + envp->data_size = len;
> > + memcpy(envp->data, data, len);
> > + free(data);
> > + }
>
> It seems you have a memory leak here. I cannot find a free(envp)
> anywhere.
envp will be returned to a caller via env_out.
> Speaking about memory... what is the overall code / data size impact
> of this patch set?
No statistics yet.
>
> > --- a/env/env.c
> > +++ b/env/env.c
> > @@ -85,12 +85,12 @@ static enum env_location env_locations[] = {
> > #endif
> > };
> >
> > -static bool env_has_inited(enum env_location location)
> > +static bool env_has_inited(enum env_context ctx, enum env_location location)
> > {
> > - return gd->env_has_init & BIT(location);
> > + return gd->env_has_init[ctx] & BIT(location);
> > }
>
> This should be re-considered. GD is a tight resource, so increasing
> it's size should be really justified. Also, I wonder if we really
> should initialize all contexts the same. We should keep in mind
> that many boards already need the (U-Boot) environment in SPL, but
> there resource usage is critical in most cases.
Yes, I think that I have not paid enough attentions to SPL case.
I will appreciate your suggestions.
> I think it would make a lot of sense to initialize only the U-Boot
> environment early (like in SPL), and delay this for all other
> contexts until U-Boot porper is running, where we have sufficient
> resources available.
Yeah, but see my comment below.
>
> > -static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
> > +static struct env_driver *env_driver_lookup(enum env_context ctx,
> > + enum env_operation op, int prio)
> > {
> > - enum env_location loc = env_get_location(op, prio);
> > + enum env_location loc;
> > struct env_driver *drv;
> >
> > + if (ctx == ENVCTX_UBOOT)
> > + loc = env_get_location(op, prio);
> > + else
> > + loc = env_get_location_ext(ctx, op, prio);
>
> This makes no sense. ENVCTX_UBOOT is just one of the available
> contexts; no "if" should be needed here.
NAK.
env_get_location is currently defined as a weak function.
So this hack is necessary, in particular, on a system where
UEFI is not enabled and env_get_location is yet overwritten.
> > - if (!drv->load)
> > + if ((ctx == ENVCTX_UBOOT && !drv->load) ||
> > + (ctx != ENVCTX_UBOOT && !drv->load_ext))
> > continue;
>
> Ditto here.
>
> > - ret = drv->load();
> > + if (ctx == ENVCTX_UBOOT)
> > + ret = drv->load();
> > + else
> > + ret = drv->load_ext(ctx);
>
> ...and here.
>
> > - env_get_location(ENVOP_LOAD, best_prio);
> > + if (ctx == ENVCTX_UBOOT)
> > + env_get_location(ENVOP_LOAD, best_prio);
> > + else
> > + env_get_location_ext(ctx, ENVOP_LOAD, best_prio);
>
> ...and here.
>
> > - if (!drv->save)
> > + if ((ctx == ENVCTX_UBOOT && !drv->save) ||
> > + (ctx != ENVCTX_UBOOT && !drv->save_ext))
> > return -ENODEV;
>
> ...and here.
>
> > - if (!env_has_inited(drv->location))
> > + if (!env_has_inited(ctx, drv->location))
> > return -ENODEV;
>
> ...and here.
>
> > - ret = drv->save();
> > + if (ctx == ENVCTX_UBOOT)
> > + ret = drv->save();
> > + else
> > + ret = drv->save_ext(ctx);
>
> ...and here.
>
>
> > int env_init(void)
> > {
> > struct env_driver *drv;
> > int ret = -ENOENT;
> > - int prio;
> > + int ctx, prio;
>
> Error. Context is an enum.
>
> > + /* other than ENVCTX_UBOOT */
> > + for (ctx = 1; ctx < ENVCTX_COUNT; ctx++) {
>
> Ugly code. "ctx" is an enum, so "1" is a magic number here that
> should not be used.
There are already bunch of code where enum is interchangeably
used as an integer.
> Please fix.
>
> > - for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
> > + /* ENVCTX_UBOOT */
> > + ret = -ENOENT;
> > + for (prio = 0; (drv = env_driver_lookup(ENVCTX_UBOOT, ENVOP_INIT,
> > + prio));
> > + prio++) {
>
> See problem above. U-Boot context should not need separate code, it
> is just one context among others...
It seems to me that this comment is conflicting with your assertion
below.
> ...unless you really split initialization into early init (U-Boot,
> eventually already in SPL) and late init (all other contexts,
> delayed until U-Boot proper).
>
> > --- a/include/asm-generic/global_data.h
> > +++ b/include/asm-generic/global_data.h
> > @@ -20,6 +20,7 @@
> > */
> >
> > #ifndef __ASSEMBLY__
> > +#include <environment.h>
> > #include <fdtdec.h>
> > #include <membuff.h>
> > #include <linux/list.h>
> > @@ -50,8 +51,10 @@ typedef struct global_data {
> > #endif
> > unsigned long env_addr; /* Address of Environment struct */
> > unsigned long env_valid; /* Environment valid? enum env_valid */
> > - unsigned long env_has_init; /* Bitmask of boolean of struct env_location offsets */
> > - int env_load_prio; /* Priority of the loaded environment */
> > + unsigned long env_has_init[ENVCTX_COUNT_MAX];
> > + /* Bitmask of boolean of struct env_location offsets */
> > + int env_load_prio[ENVCTX_COUNT_MAX];
> > + /* Priority of the loaded environment */
>
> NAK. Please keep this information out of GD. This is a tight
> resource, we must not extend it for such purposes.
But in this way, we will have to handle contexts differently
depending on whether it is ENVCTX_UBOOT or not.
> > --- a/include/env_default.h
> > +++ b/include/env_default.h
> > @@ -15,6 +15,7 @@ env_t environment __UBOOT_ENV_SECTION__(environment) = {
> > #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
> > 1, /* Flags: valid */
> > #endif
> > + ENV_SIZE,
>
> Full NAK!!!
>
> You *MUST NOT* change the data structure of the environment on
> external storage, as this breaks compatibility with all existing
> systems. This is a strict no-go.
Let me think, but I don't have a good idea yet.
> > diff --git a/include/environment.h b/include/environment.h
> > index cd966761416e..9fa085a9b728 100644
> > --- a/include/environment.h
> > +++ b/include/environment.h
> > @@ -134,21 +134,31 @@ extern unsigned long nand_env_oob_offset;
> > #include "compiler.h"
> >
> > #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
> > -# define ENV_HEADER_SIZE (sizeof(uint32_t) + 1)
> > -
> > # define ACTIVE_FLAG 1
> > # define OBSOLETE_FLAG 0
> > +
> > +#define ENVIRONMENT_HEADER \
> > + uint32_t crc; /* CRC32 over data bytes */ \
> > + uint32_t data_size; /* Environment data size */ \
> > + unsigned char flags; /* active/obsolete flags */
> > #else
> > -# define ENV_HEADER_SIZE (sizeof(uint32_t))
> > +#define ENVIRONMENT_HEADER \
> > + uint32_t crc; /* CRC32 over data bytes */ \
> > + uint32_t data_size; /* Environment data size */
> > #endif
> >
> > +typedef struct environment_hdr {
> > + ENVIRONMENT_HEADER
> > + unsigned char data[]; /* Environment data */
> > +} env_hdr_t;
> > +
> > +# define ENV_HEADER_SIZE (sizeof(env_hdr_t))
> > +
> > +/* For compatibility */
> > #define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
> >
> > typedef struct environment_s {
> > - uint32_t crc; /* CRC32 over data bytes */
> > -#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
> > - unsigned char flags; /* active/obsolete flags */
> > -#endif
> > + ENVIRONMENT_HEADER
> > unsigned char data[ENV_SIZE]; /* Environment data */
> > } env_t;
>
> Full NAK!!!
>
> You *MUST NOT* change the data structure of the environment on
> external storage, as this breaks compatibility with all existing
> systems. This is a strict no-go.
>
> > */
> > int (*load)(void);
> >
> > + /**
> > + * load_ext() - Load given environment context from storage
> > + *
> > + * This method is optional. If not provided, no environment will be
> > + * loaded.
> > + *
> > + * @ctx: context to be loaded
> > + * Return: 0 if OK, -ve on error
> > + */
> > + int (*load_ext)(enum env_context ctx);
> > +
> > /**
> > * save() - Save the environment to storage
> > *
> > @@ -225,6 +251,16 @@ struct env_driver {
> > */
> > int (*save)(void);
> >
> > + /**
> > + * save_ext() - Save given environment context to storage
> > + *
> > + * This method is required for 'saveenv' to work.
> > + *
> > + * @ctx: context to be saved
> > + * Return: 0 if OK, -ve on error
> > + */
> > + int (*save_ext)(enum env_context ctx);
> > +
> > /**
> > * init() - Set up the initial pre-relocation environment
> > *
> > @@ -234,6 +270,17 @@ struct env_driver {
> > * other -ve on error
> > */
> > int (*init)(void);
> > +
> > + /**
> > + * init() - Set up the initial pre-relocation environment
> > + *
> > + * This method is optional.
> > + *
> > + * @ctx: context to be saved
> > + * @return 0 if OK, -ENOENT if no initial environment could be found,
> > + * other -ve on error
> > + */
> > + int (*init_ext)(enum env_context ctx);
>
> NAK. We should ot need always pairs of functions foo() and
> foo_ext(). There should always be only foo(), which supports
> contexts.
This is a transition state as I mentioned in "known issues/TODOs"
in my cover letter.
If you agree, I can remove all the existing interfaces
but only when all the storage drivers are converted.
Thanks,
-Takahiro Akashi
> > /* Import from binary representation into hash table */
> > int env_import(const char *buf, int check);
> > +int env_import_ext(const char *buf, enum env_context ctx, int check);
> >
> > /* Export from hash table into binary representation */
> > int env_export(env_t *env_out);
> > +int env_export_ext(env_hdr_t **env_out, enum env_context ctx);
> >
> > #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
> > /* Select and import one of two redundant environments */
> > int env_import_redund(const char *buf1, int buf1_status,
> > const char *buf2, int buf2_status);
> > +int env_import_redund_ext(const char *buf1, int buf1_status,
> > + const char *buf2, int buf2_status,
> > + enum env_context ctx);
> > #endif
>
> Ditto here and following.
>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
> panic: kernel trap (ignored)
More information about the U-Boot
mailing list