[PATCH v3 2/5] kexec: abort liveupdate handover on kernel_kexec() unwind

Pasha Tatashin pasha.tatashin at soleen.com
Mon Mar 23 07:12:21 PDT 2026


On Sat, Mar 21, 2026 at 7:05 PM Pasha Tatashin
<pasha.tatashin at soleen.com> wrote:
>
> On Sat, Mar 21, 2026 at 10:39 AM Oskar Gerlicz Kowalczuk
> <oskar at gerlicz.space> wrote:
> >
> > liveupdate_reboot() can freeze outgoing session state before the kexec
> > transition completes. v2 only unwound that state when kernel_kexec()
> > returned a non-zero error, which misses the preserve_context path where
> > machine_kexec() can return to the old kernel with error == 0.
> >
> > If that return path skips the abort, outgoing sessions remain stuck in
> > the reboot state and later user space operations cannot make progress.
> >
> > Treat every return to the original kernel after a prepared liveupdate
> > handover as an unwind point and call liveupdate_reboot_abort() there as
> > well.
> >
> > Fixes: 8b32d3ced269 ("kexec: abort liveupdate handover on kernel_kexec() unwind")
> > Signed-off-by: Oskar Gerlicz Kowalczuk <oskar at gerlicz.space>
>
> AFAIK, preserve-context reboot is no longer used; in fact, it is
> currently broken. I tried using it six years ago and hit an old
> spinlock bug in the x86 code that prevented it, which means this path
> touches dead code.
>
> It was originally developed for machines lacking hardware support for
> hibernation, and I can't imagine anyone using it today. (I know Amazon
> is using it for a non intended reason and that is a very specific
> scenario in a costumized kernel that internally fixes the upstream
> bugs, and would not conlict with live update).
>
> During live update, we cannot return to userspace after a successful
> liveupdate_reboot(). We specifically place liveupdate_reboot() in the
> kexec path as the last possible point of failure. Perhap, to guarantee
> this behavior, we could  add !KEXEC_JUMP as a dependency to KHO and
> LUO or crash the kernel.

So, I think the right approach to address this issue is simply skip
liveupdate_reboot() call when we are doing preserve context kexec.
With preserve context we are going to get back to the current kernel,
there is absolutly no need to do serialization. So something like this
should work:
https://git.kernel.org/pub/scm/linux/kernel/git/tatashin/linux.git/commit/?h=luo-reboot-sync/rfc/1&id=b13cf19ebbf2f8483a3c5a61a7358611b391edf6

>
> Pasha
>
> > ---
> >  include/linux/liveupdate.h       |  5 +++++
> >  kernel/kexec_core.c              |  6 +++++-
> >  kernel/liveupdate/luo_core.c     | 11 ++++++++++-
> >  kernel/liveupdate/luo_internal.h |  1 +
> >  kernel/liveupdate/luo_session.c  | 23 +++++++++++++++++++++++
> >  5 files changed, 44 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
> > index dd11fdc76a5f..d93b043a0421 100644
> > --- a/include/linux/liveupdate.h
> > +++ b/include/linux/liveupdate.h
> > @@ -226,6 +226,7 @@ bool liveupdate_enabled(void);
> >
> >  /* Called during kexec to tell LUO that entered into reboot */
> >  int liveupdate_reboot(void);
> > +void liveupdate_reboot_abort(void);
> >
> >  int liveupdate_register_file_handler(struct liveupdate_file_handler *fh);
> >  int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh);
> > @@ -250,6 +251,10 @@ static inline int liveupdate_reboot(void)
> >         return 0;
> >  }
> >
> > +static inline void liveupdate_reboot_abort(void)
> > +{
> > +}
> > +
> >  static inline int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
> >  {
> >         return -EOPNOTSUPP;
> > diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
> > index 2fea396d29b9..95710a7d5e56 100644
> > --- a/kernel/kexec_core.c
> > +++ b/kernel/kexec_core.c
> > @@ -1139,6 +1139,7 @@ bool kexec_load_permitted(int kexec_image_type)
> >  int kernel_kexec(void)
> >  {
> >         int error = 0;
> > +       bool liveupdate_prepared = false;
> >
> >         if (!kexec_trylock())
> >                 return -EBUSY;
> > @@ -1147,6 +1148,7 @@ int kernel_kexec(void)
> >                 goto Unlock;
> >         }
> >
> > +       liveupdate_prepared = true;
> >         error = liveupdate_reboot();
> >         if (error)
> >                 goto Unlock;
> > @@ -1230,7 +1232,9 @@ int kernel_kexec(void)
> >         }
> >  #endif
> >
> > - Unlock:
> > +Unlock:
> > +       if (liveupdate_prepared)
> > +               liveupdate_reboot_abort();
> >         kexec_unlock();
> >         return error;
> >  }
> > diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
> > index dda7bb57d421..95a0b81ce60d 100644
> > --- a/kernel/liveupdate/luo_core.c
> > +++ b/kernel/liveupdate/luo_core.c
> > @@ -233,8 +233,9 @@ int liveupdate_reboot(void)
> >         err = kho_finalize();
> >         if (err) {
> >                 pr_err("kho_finalize failed %d\n", err);
> > +               liveupdate_reboot_abort();
> >                 /*
> > -                * kho_finalize() may return libfdt errors, to aboid passing to
> > +                * kho_finalize() may return libfdt errors, to avoid passing to
> >                  * userspace unknown errors, change this to EAGAIN.
> >                  */
> >                 err = -EAGAIN;
> > @@ -243,6 +244,14 @@ int liveupdate_reboot(void)
> >         return err;
> >  }
> >
> > +void liveupdate_reboot_abort(void)
> > +{
> > +       if (!liveupdate_enabled())
> > +               return;
> > +
> > +       luo_session_abort_reboot();
> > +}
> > +
> >  /**
> >   * liveupdate_enabled - Check if the live update feature is enabled.
> >   *
> > diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> > index 0cfc0269d746..8a6b1f6c9b4f 100644
> > --- a/kernel/liveupdate/luo_internal.h
> > +++ b/kernel/liveupdate/luo_internal.h
> > @@ -88,6 +88,7 @@ int luo_session_retrieve(const char *name, struct file **filep);
> >  int __init luo_session_setup_outgoing(void *fdt);
> >  int __init luo_session_setup_incoming(void *fdt);
> >  int luo_session_serialize(void);
> > +void luo_session_abort_reboot(void);
> >  int luo_session_deserialize(void);
> >  bool luo_session_quiesce(void);
> >  void luo_session_resume(void);
> > diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
> > index d97ec91e1118..200dd3b8229c 100644
> > --- a/kernel/liveupdate/luo_session.c
> > +++ b/kernel/liveupdate/luo_session.c
> > @@ -788,6 +788,29 @@ int luo_session_serialize(void)
> >         return err;
> >  }
> >
> > +void luo_session_abort_reboot(void)
> > +{
> > +       struct luo_session_header *sh = &luo_session_global.outgoing;
> > +       struct luo_session *session;
> > +       int i = 0;
> > +
> > +       guard(rwsem_write)(&sh->rwsem);
> > +       if (!READ_ONCE(sh->rebooting))
> > +               return;
> > +
> > +       list_for_each_entry(session, &sh->list, list) {
> > +               if (i >= sh->header_ser->count)
> > +                       break;
> > +
> > +               luo_session_unfreeze_one(session, &sh->ser[i]);
> > +               memset(&sh->ser[i], 0, sizeof(sh->ser[i]));
> > +               i++;
> > +       }
> > +
> > +       sh->header_ser->count = 0;
> > +       luo_session_reboot_done(sh);
> > +}
> > +
> >  /**
> >   * luo_session_quiesce - Ensure no active sessions exist and lock session lists.
> >   *
> > --
> > 2.53.0
> >
> >



More information about the kexec mailing list