Hi Akihiko,

Thanks for the review.

On Sat, Jul 26, 2025 at 09:48:12PM +0900, Akihiko Odaki wrote:
> On 2025/07/25 21:19, Arun Menon wrote:
> > - We need to have good error reporting in the callbacks in
> >    VMStateDescription struct. Specifically pre_save, post_save,
> >    pre_load and post_load callbacks.
> > - It is not possible to change these functions everywhere in one
> >    patch, therefore, we introduce a duplicate set of callbacks
> >    with Error object passed to them.
> > - So, in this commit, we implement 'errp' variants of these callbacks,
> >    introducing an explicit Error object parameter.
> > - This is a functional step towards transitioning the entire codebase
> >    to the new error-parameterized functions.
> > - Deliberately called in mutual exclusion from their counterparts,
> >    to prevent conflicts during the transition.
> > - New impls should preferentally use 'errp' variants of
> >    these methods, and existing impls incrementally converted.
> >    The variants without 'errp' are intended to be removed
> >    once all usage is converted.
> > 
> > Signed-off-by: Arun Menon <arme...@redhat.com>
> > ---
> >   include/migration/vmstate.h | 11 ++++++++
> >   migration/vmstate.c         | 62 
> > +++++++++++++++++++++++++++++++++++++++------
> >   2 files changed, 65 insertions(+), 8 deletions(-)
> > 
> > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > index 
> > 056781b1c21e737583f081594d9f88b32adfd674..53fa72c1bbde399be02c88fc8745fdbb79bfd7c8
> >  100644
> > --- a/include/migration/vmstate.h
> > +++ b/include/migration/vmstate.h
> > @@ -200,15 +200,26 @@ struct VMStateDescription {
> >        * exclusive. For this reason, also early_setup VMSDs are migrated in 
> > a
> >        * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated 
> > in
> >        * a QEMU_VM_SECTION_START section.
> > +     *
> > +     * There are duplicate impls of the post/pre save/load hooks.
> > +     * New impls should preferentally use 'errp' variants of these
> > +     * methods and existing impls incrementally converted.
> > +     * The variants without 'errp' are intended to be removed
> > +     * once all usage is converted.
> >        */
> > +
> >       bool early_setup;
> >       int version_id;
> >       int minimum_version_id;
> >       MigrationPriority priority;
> >       int (*pre_load)(void *opaque);
> > +    int (*pre_load_errp)(void *opaque, Error **errp);
> >       int (*post_load)(void *opaque, int version_id);
> > +    int (*post_load_errp)(void *opaque, int version_id, Error **errp);
> >       int (*pre_save)(void *opaque);
> > +    int (*pre_save_errp)(void *opaque, Error **errp);
> >       int (*post_save)(void *opaque);
> > +    int (*post_save_errp)(void *opaque, Error **errp);
> >       bool (*needed)(void *opaque);
> >       bool (*dev_unplug_pending)(void *opaque);
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index 
> > bb5e9bf38d6ee7619ceb3e9da29209581c3c12eb..e427ef49b2b1991b0a3cdb14d641c197e00014b0
> >  100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -152,7 +152,16 @@ int vmstate_load_state(QEMUFile *f, const 
> > VMStateDescription *vmsd,
> >           trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
> >           return -EINVAL;
> >       }
> > -    if (vmsd->pre_load) {
> > +    if (vmsd->pre_load_errp) {
> > +        ret = vmsd->pre_load_errp(opaque, errp);
> > +        if (ret) {
> > +            error_prepend(errp, "VM pre load failed for: '%s', "
> > +                          "version_id: '%d', minimum version_id: '%d', "
> > +                          "ret: %d: ", vmsd->name, vmsd->version_id,
> > +                          vmsd->minimum_version_id, ret);
> > +            return ret;
> > +        }
> > +    } else if (vmsd->pre_load) {
> >           ret = vmsd->pre_load(opaque);
> >           if (ret) {
> >               error_setg(errp, "VM pre load failed for: '%s', "
> > @@ -236,7 +245,14 @@ int vmstate_load_state(QEMUFile *f, const 
> > VMStateDescription *vmsd,
> >           qemu_file_set_error(f, ret);
> >           return ret;
> >       }
> > -    if (vmsd->post_load) {
> > +    if (vmsd->post_load_errp) {
> > +        ret = vmsd->post_load_errp(opaque, version_id, errp);
> > +        if (ret < 0) {
> > +            error_prepend(errp, "VM Post load failed for: %s, version_id: 
> > %d, "
> > +                          "minimum_version: %d, ret: %d: ", vmsd->name,
> > +                          vmsd->version_id, vmsd->minimum_version_id, ret);
> > +        }
> > +    } else if (vmsd->post_load) {
> >           ret = vmsd->post_load(opaque, version_id);
> >           if (ret < 0) {
> >               error_setg(errp,
> > @@ -412,11 +428,19 @@ int vmstate_save_state_v(QEMUFile *f, const 
> > VMStateDescription *vmsd,
> >                            void *opaque, JSONWriter *vmdesc, int 
> > version_id, Error **errp)
> >   {
> >       int ret = 0;
> > +    Error *local_err = NULL;
> >       const VMStateField *field = vmsd->fields;
> >       trace_vmstate_save_state_top(vmsd->name);
> > -    if (vmsd->pre_save) {
> > +    if (vmsd->pre_save_errp) {
> > +        ret = vmsd->pre_save_errp(opaque, errp);
> > +        trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> > +        if (ret) {
> > +            error_prepend(errp, "pre-save failed: %s: ", vmsd->name);
> > +            return ret;
> > +        }
> > +    } else if (vmsd->pre_save) {
> >           ret = vmsd->pre_save(opaque);
> >           trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> >           if (ret) {
> > @@ -524,10 +548,22 @@ int vmstate_save_state_v(QEMUFile *f, const 
> > VMStateDescription *vmsd,
> >                   }
> >                   if (ret) {
> > -                    error_setg(errp, "Save of field %s/%s failed",
> > -                                vmsd->name, field->name);
> > -                    if (vmsd->post_save) {
> > -                        vmsd->post_save(opaque);
> > +                    if (*errp == NULL) {
> 
> include/qapi/error.h says:
> >  * - You may pass NULL to not receive the error, &error_abort to abort
> >  *   on error, &error_fatal to exit(1) on error, or a pointer to a
> >  *   variable containing NULL to receive the error.
> 
> >  * - The function may pass @errp to functions it calls to pass on
> >  *   their errors to its caller.  If it dereferences @errp to check
> >  *   for errors, it must use ERRP_GUARD().

Yes, we can add an ERRP_GUARD() before the null check (*errp == NULL), iff we
need a check, more on that below.
> 
> I also think this change is irrelevant with the addition of the new 'errp'
> variants; it fixes an assertion failure when vmstate_save_state_v() failed
> and set errp. It is not a new problem caused by the 'errp' variants.
> 
You are right.

vmstate_save_state() function, inturn calls vmstate_save_state_v() with NULL 
errp.
So that can be ignored for now, as the errors will not be set in errp at all.
Calling error_setg() exclusively later, will not cause any trouble.

On the other hand,
vmstate_save_state_v() will set errp.
And I thought re-setting it here, using error_setg() will crash QEMU.
commit 848a0503422d043d541130d5e3e2f7bc147cdef9 introduced error_setg()
in place of error_report() 

However, on review, I do think nothing has changed indeed as far as
this patchset is concerned. I do wonder though, how resetting this
does not crash QEMU. Maybe I am missing something.

> If that's true, this change should have its own explanation in the patch
> message, and also be split into another patch as "a commit message that
> mentions 'Also, ...' is often a good candidate for splitting into multiple
> patches." (docs/devel/submitting-a-patch.rst)
> 
> > +                        error_setg(errp, "Save of field %s/%s failed",
> > +                                   vmsd->name, field->name);
> > +                    }
> > +                    if (vmsd->post_save_errp) {
> > +                        int ps_ret = vmsd->post_save_errp(opaque, 
> > &local_err);
> > +                        if (ps_ret < 0) {
> 
> The error checks else this and next ps_ret checks only care if it's zero or
> not, but this checks for the sign, leading to inconsistency.

Yes, I shall keep it consistent, a non-zero value means failure. No need of 
sign check.
> 
> > +                            error_free_or_abort(errp);
> > +                            error_propagate(errp, local_err);
> > +                            ret = ps_ret;
> > +                        }
> > +                    } else if (vmsd->post_save) {
> > +                        int ps_ret = vmsd->post_save(opaque);
> > +                        if (ps_ret < 0) {
> > +                            ret = ps_ret;
> > +                        }
> >                       }
> >                       return ret;
> >                   }
> > @@ -554,7 +590,17 @@ int vmstate_save_state_v(QEMUFile *f, const 
> > VMStateDescription *vmsd,
> >       ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> > -    if (vmsd->post_save) {
> > +    if (vmsd->post_save_errp) {
> > +        int ps_ret = vmsd->post_save_errp(opaque, &local_err);
> > +        if (!ret && ps_ret) {
> > +            ret = ps_ret;
> > +            error_propagate(errp, local_err);
> > +        } else if (ret && ps_ret) {
> > +            error_free_or_abort(errp);
> > +            error_propagate(errp, local_err);
> > +            ret = ps_ret;
> > +        }
> 
> Simpler:
> 
> if (ps_ret) {
>     if (ret) {
>       error_free_or_abort(errp);
>     }
>     ret = ps_ret;
>     error_propagate(errp, local_err);
> }
> 
Will do.
> > +    } else if (vmsd->post_save) {
> >           int ps_ret = vmsd->post_save(opaque);
> >           if (!ret && ps_ret) {
> >               ret = ps_ret;
> 
> When there is a preceding error, this code still returns it and dismisses
> the post_save() error although the other part of this function is changed to
> propagate the error of post-save unconditionally. Please keep them
> consistent.

I do feel that in the new implementation (post_save_errp), we should be 
dismissing
the preceeding error and propagating the new error from post_save(). 
Because, that way, it makes sense to run the post_save_errp() part even after 
encountering
an error in the preceeding section (vmstate_subsection_save).

Not sure if I should change the old impl post_save to match post_save_errp,
Or should I match the new impl with the old one.
> 
> > 
> 


Regards,
Arun


Reply via email to