[U-Boot] [RFC, PATCH v4 02/16] env: extend interfaces to import/export U-Boot environment per context
Wolfgang Denk
wd at denx.de
Fri Jul 19 10:04:05 UTC 2019
Dear Takahiro,
In message <20190719081556.GR21948 at linaro.org> you wrote:
>
> 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?
I agee with the interface, but not with the new names. We should
use the existing names, and not change them. Only add new functions
where needed.
> > > + 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.)
I was talking about external storage - there you can have only one
context in a data block. Internally (in the hash table), the context
should probably be art of the key, so there cannot be any such
conflicts either.
> > > - 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.
It should be handled in a single, generic way, and we allow that a
context defines his own implementation (say, through weak default
handlers which can be overwritten by context-specific code).
> > > +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.
OK - but this shows clealy the disadvantages of defining new names.
Please get rid of all this _ext stuff...
> > 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.
Yes, but where does it get freed?
> > Speaking about memory... what is the overall code / data size impact
> > of this patch set?
>
> No statistics yet.
Can you please provide some?
> > > + 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.
Well, this mechanism need to be adapted for contexts, then It may
indeed makle sense to provide the same overwrite possibiltiy for
each context.
> > > - 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.
All this code _begs_ for cleanup.
> >
> > > 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.
There is no good excuse for following bad examples. There is not
even any documentation that mentions that ENVCTX_UBOOT has to be the
first entry in the enum.
> > > - 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.
No. You can still iterate over the whole enum, and in the look
dosomething like "if (CTX == ENVCTX_UBOOT) continue;" or such,
without relying on a specific numeric value. This is much more
reliable.
And you can also extend this with proper testing for running in SPL
ir not, etc.
> > 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.
Yes, indeed, U-Boot environment may be handled differently, but we
can still share the same code.
> > > --- 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.
You can only _pre_pend the size field in a bigger structure, which
has size first, followed by the existing struct env.
> > 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.
Adding the additional context parameter seems not so much a problem.
This can be a single big commit including all the drivers, which get
passed a '0' argument which they may ignore. The introdduce
contexts as a scond step.
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
It's hard to think of you as the end result of millions of years of
evolution.
More information about the U-Boot
mailing list