[PATCH v7 04/20] kernel: Add combined power-off+restart handler call chain API

Dmitry Osipenko dmitry.osipenko at collabora.com
Sun Apr 17 18:44:26 PDT 2022


On 4/15/22 21:14, Rafael J. Wysocki wrote:
> Honestly, I would prefer this to be split so as to make it easier to
> review if nothing else.

I'll try to split it in v8.

> On Tue, Apr 12, 2022 at 1:39 AM Dmitry Osipenko
> <dmitry.osipenko at collabora.com> wrote:
>>
>> SoC platforms often have multiple ways of how to perform system's
>> power-off and restart operations. Meanwhile today's kernel is limited to
>> a single option. Add combined power-off+restart handler call chain API,
>> which is inspired by the restart API. The new API provides both power-off
>> and restart functionality.
>>
>> The old pm_power_off method will be kept around till all users are
>> converted to the new API.
>>
>> Current restart API will be replaced by the new unified API since
>> new API is its superset. The restart functionality of the sys-off handler
>> API is built upon the existing restart-notifier APIs.
> 
> Which means that the existing notifier chains for system restart are
> used as they are without modifications.
> 
> At least that's what follows from the code and it would be good to
> mention it here.

Will improve the commit message.

> Moreover, a new notifier chain is introduced for the power-off case
> and it appears to be the counterpart of the restart_handler_list
> chain, but then why is it blocking and not atomic like the latter?

Good catch, it probably indeed should be atomic because shutting down
could run with a disabled interrupts. I'll invistigate this more for v8,
at least right now I don't recall any particular reason for using the
blocking notifier.

>> In order to ease conversion to the new API, convenient helpers are added
>> for the common use-cases. They will reduce amount of boilerplate code and
>> remove global variables. These helpers preserve old behaviour for cases
>> where only one power-off handler is expected, this is what all existing
>> drivers want, and thus, they could be easily converted to the new API.
>> Users of the new API should explicitly enable power-off chaining by
>> setting corresponding flag of the power_handler structure.
> 
> "the corresponding"

Thanks

>> Signed-off-by: Dmitry Osipenko <dmitry.osipenko at collabora.com>
>> ---
>>  include/linux/reboot.h   | 229 ++++++++++++++-
>>  kernel/power/hibernate.c |   2 +-
>>  kernel/reboot.c          | 604 ++++++++++++++++++++++++++++++++++++++-
>>  3 files changed, 827 insertions(+), 8 deletions(-)
>>
>> diff --git a/include/linux/reboot.h b/include/linux/reboot.h
>> index a2429648d831..ba5e5dddcfcd 100644
>> --- a/include/linux/reboot.h
>> +++ b/include/linux/reboot.h
>> @@ -8,10 +8,35 @@
>>
>>  struct device;
>>
>> -#define SYS_DOWN       0x0001  /* Notify of system down */
>> -#define SYS_RESTART    SYS_DOWN
>> -#define SYS_HALT       0x0002  /* Notify of system halt */
>> -#define SYS_POWER_OFF  0x0003  /* Notify of system power off */
>> +enum reboot_prepare_mode {
>> +       SYS_DOWN = 1,           /* Notify of system down */
>> +       SYS_RESTART = SYS_DOWN,
>> +       SYS_HALT,               /* Notify of system halt */
>> +       SYS_POWER_OFF,          /* Notify of system power off */
>> +};
>> +
>> +/*
>> + * Standard restart priority levels. Intended to be set in the
>> + * sys_off_handler.restart_priority field.
>> + *
>> + * Use `RESTART_PRIO_ABC +- prio` style for additional levels.
>> + *
>> + * RESTART_PRIO_RESERVED:      Falls back to RESTART_PRIO_DEFAULT.
>> + *                             Drivers may leave priority initialized
>> + *                             to zero, to auto-set it to the default level.
> 
> What is the "default level" here?

"default level" = RESTART_PRIO_DEFAULT = 128

I'll remove the second sentence about the "default level", for clarity.

>> + *
>> + * RESTART_PRIO_LOW:           Use this for handler of last resort.
>> + *
>> + * RESTART_PRIO_DEFAULT:       Use this for default/generic handler.
>> + *
>> + * RESTART_PRIO_HIGH:          Use this if you have multiple handlers and
>> + *                             this handler has higher priority than the
>> + *                             default handler.
>> + */
>> +#define RESTART_PRIO_RESERVED          0
>> +#define RESTART_PRIO_LOW               8
>> +#define RESTART_PRIO_DEFAULT           128
>> +#define RESTART_PRIO_HIGH              192
>>
>>  enum reboot_mode {
>>         REBOOT_UNDEFINED = -1,
>> @@ -49,6 +74,201 @@ extern int register_restart_handler(struct notifier_block *);
>>  extern int unregister_restart_handler(struct notifier_block *);
>>  extern void do_kernel_restart(char *cmd);
>>
>> +/*
>> + * System power-off and restart API.
>> + */
>> +
>> +/*
>> + * Standard power-off priority levels. Intended to be set in the
>> + * sys_off_handler.power_off_priority field.
>> + *
>> + * Use `POWEROFF_PRIO_ABC +- prio` style for additional levels.
> 
> What exactly does this mean?

"POWEROFF_PRIO_DEFAULT+1 or POWEROFF_PRIO_DEFAULT-1" for example, I'll
improve it in v8.

>> + *
>> + * POWEROFF_PRIO_RESERVED:     Falls back to POWEROFF_PRIO_DEFAULT.
>> + *                             Drivers may leave priority initialized
>> + *                             to zero, to auto-set it to the default level.
>> + *
>> + * POWEROFF_PRIO_PLATFORM:     Intended to be used by platform-level handler.
>> + *                             Has lowest priority since device drivers are
>> + *                             expected to take over platform handler which
>> + *                             doesn't allow further callback chaining.
>> + *
>> + * POWEROFF_PRIO_DEFAULT:      Use this for default/generic handler.
>> + *
>> + * POWEROFF_PRIO_FIRMWARE:     Use this if handler uses firmware call.
>> + *                             Has highest priority since firmware is expected
>> + *                             to know best how to power-off hardware properly.
>> + */
>> +#define POWEROFF_PRIO_RESERVED         0
>> +#define POWEROFF_PRIO_PLATFORM         1
>> +#define POWEROFF_PRIO_DEFAULT          128
>> +#define POWEROFF_PRIO_HIGH             192
>> +#define POWEROFF_PRIO_FIRMWARE         224
>> +
>> +enum poweroff_mode {
>> +       POWEROFF_NORMAL = 0,
> 
> Why not just POWEROFF?

This is a bit too generic name to me.

I can rename it as MODE_POWEROFF and MODE_POWEROFF_PREPARE.

>> +       POWEROFF_PREPARE,
>> +};
>> +
>> +/**
>> + * struct power_off_data - Power-off callback argument
>> + *
>> + * @cb_data: Callback data.
>> + */
>> +struct power_off_data {
>> +       void *cb_data;
>> +};
>> +
>> +/**
>> + * struct power_off_prep_data - Power-off preparation callback argument
>> + *
>> + * @cb_data: Callback data.
>> + */
>> +struct power_off_prep_data {
>> +       void *cb_data;
>> +};
> 
> Why does this need to be a separate data type?

To allow us extend the "struct power_off_prep_data" with more parameters
later on without a need to update each driver with the new arguments.

>> +
>> +/**
>> + * struct restart_data - Restart callback argument
>> + *
>> + * @cb_data: Callback data.
>> + * @cmd: Restart command string.
>> + * @stop_chain: Further lower priority callbacks won't be executed if set to
>> + *             true. Can be changed within callback. Default is false.
>> + * @mode: Reboot mode ID.
>> + */
>> +struct restart_data {
>> +       void *cb_data;
>> +       const char *cmd;
>> +       bool stop_chain;
>> +       enum reboot_mode mode;
>> +};
>> +
>> +/**
>> + * struct reboot_prep_data - Reboot and shutdown preparation callback argument
>> + *
>> + * @cb_data: Callback data.
>> + * @cmd: Restart command string.
>> + * @stop_chain: Further lower priority callbacks won't be executed if set to
>> + *             true. Can be changed within callback. Default is false.
>> + * @mode: Preparation mode ID.
>> + */
>> +struct reboot_prep_data {
>> +       void *cb_data;
>> +       const char *cmd;
>> +       bool stop_chain;
>> +       enum reboot_prepare_mode mode;
>> +};
> 
> There seem to be some duplicate data items between struct restart_data
> and struct reboot_prep_data, so what's the reason why they are
> separate?

They indeed look similar, but have different purposes. We shouldn't use
reboot handler for restarting, hence why should we mix them?

>> +struct sys_off_handler_private_data {
>> +       struct notifier_block power_off_nb;
>> +       struct notifier_block restart_nb;
>> +       struct notifier_block reboot_nb;
> 
> So restart_nb is going to be added to restart_handler_list, eboot_nb
> will be added to reboot_notifier_list (which both exist already) and
> power_off_nb will be added to the new power_off_handler_list, right?
> 
> Of course, this means that reboot_nb will be used in
> kernel_restart_prepare() and kernel_shutdown_prepare(), so the
> corresponding callback will be invoked in both the restart and
> power-off cases.
> 
> It would be good to document that somehow.

This is documented in the doc-comment to struct sys_off_handler later in
this patch. You actually found it below.

>> +       void (*platform_power_off_cb)(void);
>> +       void (*simple_power_off_cb)(void *data);
>> +       void *simple_power_off_cb_data;
> 
> Is there any particular reason to put these callbacks here and not
> directly into struct sys_off_handler?

They are used internally by reboot.c and shouldn't be touched by the
kernel API users, so I wanted to factor out and protect all the private
data.

>> +       bool registered;
>> +};
>> +
>> +/**
>> + * struct sys_off_handler - System power-off and restart handler
>> + *
>> + * @cb_data: Pointer to user's data.
>> + *
>> + * @power_off_cb: Callback that powers off this machine. Inactive if NULL.
>> + *
>> + * @power_off_prepare_cb: Power-off preparation callback. All power-off
>> + * preparation callbacks are invoked after @reboot_prepare_cb and before
>> + * @power_off_cb. Inactive if NULL.
>> + *
>> + * @power_off_priority: Power-off callback priority, must be unique.
>> + * Zero value is reserved and auto-reassigned to POWEROFF_PRIO_DEFAULT.
>> + * Inactive if @power_off_cb is NULL.
>> + *
>> + * @power_off_chaining_allowed: Set to false if callback's execution should
>> + * stop when @power_off_cb fails to power off this machine. True if further
>> + * lower priority power-off callback should be executed. False is default
>> + * value.
>> + *
>> + * @restart_cb: Callback that reboots this machine. Inactive if NULL.
>> + *
>> + * @restart_priority: Restart callback priority, must be unique. Zero value
>> + * is reserved and auto-reassigned to RESTART_PRIO_DEFAULT. Inactive if
>> + * @restart_cb is NULL.
>> + *
>> + * @restart_chaining_disallowed: Set to true if callback's execution should
>> + * stop when @restart_cb fails to restart this machine. False if further
>> + * lower priority restart callback should be executed. False is default
>> + * value.
>> + *
>> + * @reboot_prepare_cb: Reboot/shutdown preparation callback. All reboot
>> + * preparation callbacks are invoked before @restart_cb or @power_off_cb,
>> + * depending on the mode. It's registered with register_reboot_notifier().
>> + * The point is to remove boilerplate code from drivers which use this
>> + * callback in conjunction with the restart/power-off callbacks.
>> + *
>> + * @reboot_priority: Reboot/shutdown preparation callback priority, doesn't
>> + * need to be unique. Zero is default value. Inactive if @reboot_prepare_cb
>> + * is NULL.
> 
> It is unclear that the ->reboot_prepare_cb() callback is going to be
> used for both restart and power-off and reboot_priority is about the
> preparation phase only.
> 
> And in the preparation phase the priority may not matter that much,
> because there are users who don't care about the ordering as long as
> their stuff is called at all.
> 
> Honestly, I would change the naming here, because what it is is quite
> confusing at least to me.  Especially that "restart" and "reboot" seem
> to be used interchangeably in the comments.

These are the legacy names and they are indeed very confusing. Do you
have suggestion for a better names?

>> + *
>> + * @priv: Internal data. Shouldn't be touched.
>> + *
>> + * Describes power-off and restart handlers which are invoked by kernel
>> + * to power off or restart this machine. Supports prioritized chaining for
>> + * both restart and power-off handlers.
>> + *
>> + * Struct sys_off_handler can be static. Members of this structure must not be
>> + * altered while handler is registered.
>> + *
>> + * Fill the structure members and pass it to @register_sys_off_handler().
>> + */
>> +struct sys_off_handler {
>> +       void *cb_data;
>> +
>> +       void (*power_off_cb)(struct power_off_data *data);
>> +       void (*power_off_prepare_cb)(struct power_off_prep_data *data);
>> +       int power_off_priority;
>> +       bool power_off_chaining_allowed;
>> +
>> +       void (*restart_cb)(struct restart_data *data);
>> +       int restart_priority;
>> +       bool restart_chaining_disallowed;
>> +
>> +       void (*reboot_prepare_cb)(struct reboot_prep_data *data);
>> +       int reboot_priority;
>> +
>> +       const struct sys_off_handler_private_data priv;
> 
> Why is it const?  Because of the callbacks in there?

It's const to prevent changing/abusing of the internal data by kernel
API users. Those callbacks are private to kernel/reboot.c

> Doesn't this mean that all struct sys_off_handler need to be static?

The reboot.c is allowed to modify the private data, sys_off_handler can
be static and dynamic. There is no limitaion, I made the 'priv' member
constant only for the API users.

>> +};
>> +
>> +int register_sys_off_handler(struct sys_off_handler *handler);
>> +int unregister_sys_off_handler(struct sys_off_handler *handler);
>> +
>> +int devm_register_sys_off_handler(struct device *dev,
>> +                                 struct sys_off_handler *handler);
>> +
>> +int devm_register_prioritized_power_off_handler(struct device *dev,
>> +                                               int priority,
>> +                                               void (*callback)(void *data),
>> +                                               void *cb_data);
>> +
>> +int devm_register_simple_power_off_handler(struct device *dev,
>> +                                          void (*callback)(void *data),
>> +                                          void *cb_data);
>> +
>> +int register_platform_power_off(void (*power_off)(void));
>> +int unregister_platform_power_off(void (*power_off)(void));
>> +
>> +int devm_register_prioritized_restart_handler(struct device *dev,
>> +                                             int priority,
>> +                                             void (*callback)(struct restart_data *data),
>> +                                             void *cb_data);
>> +
>> +int devm_register_simple_restart_handler(struct device *dev,
>> +                                        void (*callback)(struct restart_data *data),
>> +                                        void *cb_data);
>> +
>> +void do_kernel_power_off(void);
>> +
>>  /*
>>   * Architecture-specific implementations of sys_reboot commands.
>>   */
>> @@ -70,6 +290,7 @@ extern void kernel_restart_prepare(char *cmd);
>>  extern void kernel_restart(char *cmd);
>>  extern void kernel_halt(void);
>>  extern void kernel_power_off(void);
>> +extern bool kernel_can_power_off(void);
>>
>>  void ctrl_alt_del(void);
>>
>> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
>> index 938d5c78b421..a9464b4a3209 100644
>> --- a/kernel/power/hibernate.c
>> +++ b/kernel/power/hibernate.c
>> @@ -665,7 +665,7 @@ static void power_down(void)
>>                 hibernation_platform_enter();
>>                 fallthrough;
>>         case HIBERNATION_SHUTDOWN:
>> -               if (pm_power_off)
>> +               if (kernel_can_power_off())
>>                         kernel_power_off();
>>                 break;
>>         }
>> diff --git a/kernel/reboot.c b/kernel/reboot.c
>> index acdae4e95061..e76e2570dcf5 100644
>> --- a/kernel/reboot.c
>> +++ b/kernel/reboot.c
>> @@ -296,6 +296,595 @@ void kernel_halt(void)
>>  }
>>  EXPORT_SYMBOL_GPL(kernel_halt);
>>
>> +/*
>> + *     Notifier list for kernel code which wants to be called
>> + *     to power off the system.
>> + */
>> +static BLOCKING_NOTIFIER_HEAD(power_off_handler_list);
>> +
>> +/*
>> + * Temporary stub that prevents linkage failure while we're in process
>> + * of removing all uses of legacy pm_power_off() around the kernel.
> 
> Nit: inconsistent comment formatting.
> 
>> + */
>> +void __weak (*pm_power_off)(void);
>> +
>> +static void dummy_pm_power_off(void)
>> +{
>> +       /* temporary stub until pm_power_off() is gone, see more below */
>> +}
>> +
>> +static struct notifier_block *pm_power_off_nb;
>> +
>> +/**
>> + *     register_power_off_handler - Register function to be called to power off
>> + *                                  the system
>> + *     @nb: Info about handler function to be called
>> + *     @nb->priority:  Handler priority. Handlers should follow the
>> + *                     following guidelines for setting priorities.
>> + *                     0:      Reserved
>> + *                     1:      Power-off handler of last resort,
>> + *                             with limited power-off capabilities
>> + *                     128:    Default power-off handler; use if no other
>> + *                             power-off handler is expected to be available,
>> + *                             and/or if power-off functionality is
>> + *                             sufficient to power-off the entire system
>> + *                     255:    Highest priority power-off handler, will
>> + *                             preempt all other power-off handlers
>> + *
>> + *     Registers a function with code to be called to power off the
>> + *     system.
> 
> Because this is only used internally in this file, I'd say what it
> does directly, that is "Add a notifier to the power-off chain used for
> powering off the system".

I'll improve it, thanks.

>> + *
>> + *     Registered functions will be called as last step of the power-off
>> + *     sequence.
>> + *
>> + *     Registered functions are expected to power off the system immediately.
>> + *     If more than one function is registered, the power-off handler priority
>> + *     selects which function will be called first.
>> + *
>> + *     Power-off handlers are expected to be registered from non-architecture
>> + *     code, typically from drivers. A typical use case would be a system
>> + *     where power-off functionality is provided through a PMIC. Multiple
>> + *     power-off handlers may exist; for example, one power-off handler might
>> + *     turn off the entire system, while another only turns off part of
>> + *     system. In such cases, the power-off handler which only disables part
>> + *     of the hardware is expected to register with low priority to ensure
>> + *     that it only runs if no other means to power off the system is
>> + *     available.
> 
> I would move the above 3 paragraphs to the description comment for
> power_off_handler_list.

Noted

>> + *
>> + *     Currently always returns zero, as blocking_notifier_chain_register()
>> + *     always returns zero.
>> + */
>> +static int register_power_off_handler(struct notifier_block *nb)
>> +{
>> +       int ret;
>> +
>> +       ret = blocking_notifier_chain_register_unique_prio(&power_off_handler_list, nb);
>> +       if (ret && ret != -EBUSY)
>> +               return ret;
>> +
>> +       if (!ret)
>> +               goto set_pm_power_off;
>> +
>> +       /*
>> +        * Handler must have unique priority. Otherwise call order is
>> +        * determined by registration order, which is unreliable.
>> +        *
>> +        * This requirement will become mandatory once all drivers
>> +        * will be converted to use new sys-off API.
>> +        */
>> +       pr_err("failed to register power-off handler using unique priority\n");
>> +
>> +       ret = blocking_notifier_chain_register(&power_off_handler_list, nb);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /*
>> +        * Some drivers check whether pm_power_off was already installed.
>> +        * Install dummy callback using new API to preserve old behaviour
>> +        * for those drivers during period of transition to the new API.
>> +        */
>> +set_pm_power_off:
>> +       if (!pm_power_off) {
>> +               pm_power_off = dummy_pm_power_off;
>> +               pm_power_off_nb = nb;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int unregister_power_off_handler(struct notifier_block *nb)
>> +{
>> +       if (nb == pm_power_off_nb) {
>> +               if (pm_power_off == dummy_pm_power_off)
>> +                       pm_power_off = NULL;
>> +
>> +               pm_power_off_nb = NULL;
>> +       }
>> +
>> +       return blocking_notifier_chain_unregister(&power_off_handler_list, nb);
>> +}
>> +
>> +static void devm_unregister_power_off_handler(void *data)
>> +{
>> +       struct notifier_block *nb = data;
>> +
>> +       unregister_power_off_handler(nb);
>> +}
>> +
>> +static int devm_register_power_off_handler(struct device *dev,
>> +                                          struct notifier_block *nb)
>> +{
>> +       int err;
>> +
>> +       err = register_power_off_handler(nb);
>> +       if (err)
>> +               return err;
>> +
>> +       return devm_add_action_or_reset(dev, devm_unregister_power_off_handler,
>> +                                       nb);
>> +}
>> +
>> +static int sys_off_handler_power_off(struct notifier_block *nb,
>> +                                    unsigned long mode, void *unused)
>> +{
>> +       struct power_off_prep_data prep_data = {};
>> +       struct sys_off_handler_private_data *priv;
>> +       struct power_off_data data = {};
>> +       struct sys_off_handler *h;
>> +       int ret = NOTIFY_DONE;
>> +
>> +       priv = container_of(nb, struct sys_off_handler_private_data, power_off_nb);
>> +       h = container_of(priv, struct sys_off_handler, priv);
>> +       prep_data.cb_data = h->cb_data;
>> +       data.cb_data = h->cb_data;
>> +
>> +       switch (mode) {
>> +       case POWEROFF_NORMAL:
>> +               if (h->power_off_cb)
>> +                       h->power_off_cb(&data);
>> +
>> +               if (priv->simple_power_off_cb)
>> +                       priv->simple_power_off_cb(priv->simple_power_off_cb_data);
>> +
>> +               if (priv->platform_power_off_cb)
>> +                       priv->platform_power_off_cb();
> 
> The invocation of the priv callbacks here confuses me quite a bit.
> 
> Can you please at least add a comment explaining this?

Sure

>> +
>> +               if (!h->power_off_chaining_allowed)
>> +                       ret = NOTIFY_STOP;
>> +
>> +               break;
>> +
>> +       case POWEROFF_PREPARE:
>> +               if (h->power_off_prepare_cb)
>> +                       h->power_off_prepare_cb(&prep_data);
>> +
>> +               break;
>> +
>> +       default:
>> +               unreachable();
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +static int sys_off_handler_restart(struct notifier_block *nb,
>> +                                  unsigned long mode, void *cmd)
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +       struct restart_data data = {};
>> +       struct sys_off_handler *h;
>> +
>> +       priv = container_of(nb, struct sys_off_handler_private_data, restart_nb);
>> +       h = container_of(priv, struct sys_off_handler, priv);
>> +
>> +       data.stop_chain = h->restart_chaining_disallowed;
>> +       data.cb_data = h->cb_data;
>> +       data.mode = mode;
>> +       data.cmd = cmd;
>> +
>> +       h->restart_cb(&data);
> 
> Wouldn't it be a bit more straightforward to allow ->restart_cb() to
> return a value being either NOTIFY_STOP or NOTIFY_DONE?

Vast majority of drivers don't use the 'stop_chain', hence it's
unnecessary boilerplate code for drivers. The code looks nicer without
the boilerplate "return NOTIFY_DONE", IMO.

>> +       return data.stop_chain ? NOTIFY_STOP : NOTIFY_DONE;
> 
> And I would prefer
> 
> if (data.stop_chain)
>         return NOTIFY_STOP;
> 
> return NOTIFY_DONE;

Okay

>> +}
>> +
>> +static int sys_off_handler_reboot(struct notifier_block *nb,
>> +                                 unsigned long mode, void *cmd)
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +       struct reboot_prep_data data = {};
>> +       struct sys_off_handler *h;
>> +
>> +       priv = container_of(nb, struct sys_off_handler_private_data, reboot_nb);
>> +       h = container_of(priv, struct sys_off_handler, priv);
>> +
>> +       data.cb_data = h->cb_data;
>> +       data.stop_chain = false;
>> +       data.mode = mode;
>> +       data.cmd = cmd;
>> +
>> +       h->reboot_prepare_cb(&data);
>> +
>> +       return data.stop_chain ? NOTIFY_STOP : NOTIFY_DONE;
> 
> And analogously here.
> 
>> +}
>> +
>> +static struct sys_off_handler_private_data *
>> +sys_off_handler_private_data(struct sys_off_handler *handler)
>> +{
>> +       return (struct sys_off_handler_private_data *)&handler->priv;
> 
> Is the cast needed to avoid a warning about "const"?

The private data is constant to prevent the API users from using it. For
internal use we need to cast out the "const". It's a compilation error
to change the constant members.

>> +}
>> +
>> +/**
>> + *     devm_register_sys_off_handler - Register system power-off/restart handler
> 
> register_sys_off_handler

Good catch!

>> + *     @dev: Device that registers handler
>> + *     @handler: System-off handler
>> + *
>> + *     Registers handler that will be called as last step of the power-off
>> + *     and restart sequences.
> 
> Not necessarily as the last step, because there may be other system
> power-off/restart handlers called after it.
> 
> I would just say "at the end of the power-off and restart sequences".
> 
> Moreover, it registers the "reboot_cb" part that is not called "at the
> end" even.
> 
> Also, because this is the function that will be used by drivers etc to
> register handlers, I would give some more information on how the
> object registered by it is going to be used to the prospective users.

I'll improve the description, thank you.

>> + *
>> + *     Returns zero on success, or error code on failure.
>> + */
>> +int register_sys_off_handler(struct sys_off_handler *handler)
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +       int err, priority;
>> +
>> +       priv = sys_off_handler_private_data(handler);
>> +
>> +       /* sanity-check whether handler is registered twice */
>> +       if (priv->registered)
>> +               return -EBUSY;
>> +
>> +       if (handler->power_off_cb || handler->power_off_prepare_cb) {
>> +               if (handler->power_off_priority == POWEROFF_PRIO_RESERVED)
>> +                       priority = POWEROFF_PRIO_DEFAULT;
> 
> I'm not sure that this helps.
> 
> I mean, why can't the users of this new API pass POWEROFF_PRIO_DEFAULT
> directly if they want "default"?

Users could pass POWEROFF_PRIO_DEFAULT, but it's a boilerplate code
which I wanted to avoid by reserving 0 for the alias to
POWEROFF_PRIO_DEFAULT=128.

This removes a need from drivers to explicitly specify the default
priorities in the code, assuming that sys_off_handler is initialized to
zero.

>> +               else
>> +                       priority = handler->power_off_priority;
>> +
>> +               priv->power_off_nb.notifier_call = sys_off_handler_power_off;
>> +               priv->power_off_nb.priority = priority;
>> +
>> +               err = register_power_off_handler(&priv->power_off_nb);
>> +               if (err)
>> +                       goto reset_sys_off_handler;
>> +       }
>> +
>> +       if (handler->restart_cb) {
>> +               if (handler->restart_priority == RESTART_PRIO_RESERVED)
>> +                       priority = RESTART_PRIO_DEFAULT;
>> +               else
>> +                       priority = handler->restart_priority;
>> +
>> +               priv->restart_nb.notifier_call = sys_off_handler_restart;
>> +               priv->restart_nb.priority = priority;
>> +
>> +               err = register_restart_handler(&priv->restart_nb);
>> +               if (err)
>> +                       goto unreg_power_off_handler;
>> +       }
>> +
>> +       if (handler->reboot_prepare_cb) {
>> +               priv->reboot_nb.notifier_call = sys_off_handler_reboot;
>> +               priv->reboot_nb.priority = handler->reboot_priority;
>> +
>> +               err = register_reboot_notifier(&priv->reboot_nb);
>> +               if (err)
>> +                       goto unreg_restart_handler;
>> +       }
>> +
>> +       priv->registered = true;
>> +
>> +       return 0;
>> +
>> +unreg_restart_handler:
>> +       if (handler->restart_cb)
>> +               unregister_restart_handler(&priv->restart_nb);
>> +
>> +unreg_power_off_handler:
>> +       if (handler->power_off_cb)
>> +               unregister_power_off_handler(&priv->power_off_nb);
>> +
>> +reset_sys_off_handler:
>> +       memset(priv, 0, sizeof(*priv));
>> +
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(register_sys_off_handler);
>> +
>> +/**
>> + *     unregister_sys_off_handler - Unregister system power-off/restart handler
>> + *     @handler: System-off handler
>> + *
>> + *     Unregisters sys-off handler. Does nothing and returns zero if handler
>> + *     is NULL.
>> + *
>> + *     Returns zero on success, or error code on failure.
>> + */
>> +int unregister_sys_off_handler(struct sys_off_handler *handler)
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +
>> +       if (!handler)
>> +               return 0;
>> +
>> +       priv = sys_off_handler_private_data(handler);
>> +
>> +       /* sanity-check whether handler is unregistered twice */
>> +       if (!priv->registered)
>> +               return -EINVAL;
>> +
>> +       if (handler->reboot_prepare_cb)
>> +               unregister_reboot_notifier(&priv->reboot_nb);
>> +
>> +       if (handler->restart_cb)
>> +               unregister_restart_handler(&priv->restart_nb);
>> +
>> +       if (handler->power_off_cb)
>> +               unregister_power_off_handler(&priv->power_off_nb);
>> +
>> +       memset(priv, 0, sizeof(*priv));
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(unregister_sys_off_handler);
>> +
>> +static void devm_unregister_sys_off_handler(void *data)
>> +{
>> +       struct sys_off_handler *handler = data;
>> +
>> +       unregister_sys_off_handler(handler);
>> +}
>> +
>> +/**
>> + *     devm_register_sys_off_handler - Register system power-off/restart handler
>> + *     @dev: Device that registers handler
>> + *     @handler: System-off handler
>> + *
>> + *     Resource-managed variant of register_sys_off_handler().
>> + *
>> + *     Returns zero on success, or error code on failure.
>> + */
>> +int devm_register_sys_off_handler(struct device *dev,
>> +                                 struct sys_off_handler *handler)
>> +{
>> +       int err;
>> +
>> +       err = register_sys_off_handler(handler);
>> +       if (err)
>> +               return err;
>> +
>> +       return devm_add_action_or_reset(dev, devm_unregister_sys_off_handler,
>> +                                       handler);
>> +}
>> +EXPORT_SYMBOL(devm_register_sys_off_handler);
>> +
>> +/**
>> + *     devm_register_prioritized_power_off_handler - Register prioritized power-off callback
>> + *     @dev: Device that registers callback
>> + *     @priority: Callback's priority
>> + *     @callback: Callback function
>> + *     @cb_data: Callback's argument
>> + *
>> + *     Registers resource-managed power-off callback with a given priority.
>> + *     It will be called as last step of the power-off sequence. Callbacks
>> + *     chaining is disabled, i.e. further lower priority callbacks won't
>> + *     be executed if this @callback will fail to execute.
>> + *
>> + *     Returns zero on success, or error code on failure.
> 
> What's the case in which this should be used instead of registering a
> full sys_off handler?

There are a lot of drivers that just want to register power-off handler
with a non-default priority and don't need to stop the chain or do
anything else special. This is a convinient helper for them.

Please note that the callback here takes only the cb_data for the
argument, while sys_off uses "struct power_off_data".

Similar for the reset of the convinient helpers.

...
>> +/**
>> + *     register_platform_power_off - Register platform-level power-off callback
>> + *     @power_off: Power-off callback
>> + *
>> + *     Registers power-off callback that will be called as last step
>> + *     of the power-off sequence. This callback is expected to be invoked
>> + *     for the last resort. Further lower priority callbacks won't be
>> + *     executed if @power_off fails. Only one platform power-off callback
>> + *     is allowed to be registered at a time.
>> + *
>> + *     Returns zero on success, or error code on failure.
> 
> What's the use case for this?

There are dozens of trivial platform/board-specific power-off handlers
in the arch/ code. Those handlers don't take any arguments, hence it's a
convinient helper that transits them to the new API neatly.

>> + */
>> +int register_platform_power_off(void (*power_off)(void))
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +       int ret = 0;
>> +
>> +       priv = sys_off_handler_private_data(&platform_power_off_handler);
>> +
>> +       spin_lock(&platform_power_off_lock);
>> +       if (priv->platform_power_off_cb)
>> +               ret = -EBUSY;
>> +       else
>> +               priv->platform_power_off_cb = power_off;
> 
> Wasn't priv supposed to be const?

It's const only for the external API users to prevent them from abusing
the internal data.

>> +       spin_unlock(&platform_power_off_lock);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = register_power_off_handler(&priv->power_off_nb);
>> +       if (ret)
>> +               priv->platform_power_off_cb = NULL;
>> +
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(register_platform_power_off);
>> +
>> +/**
>> + *     unregister_platform_power_off - Unregister platform-level power-off callback
>> + *     @power_off: Power-off callback
>> + *
>> + *     Unregisters previously registered platform power-off callback.
>> + *
>> + *     Returns zero on success, or error code on failure.
>> + */
>> +int unregister_platform_power_off(void (*power_off)(void))
>> +{
>> +       struct sys_off_handler_private_data *priv;
>> +       int ret;
>> +
>> +       priv = sys_off_handler_private_data(&platform_power_off_handler);
>> +
>> +       if (priv->platform_power_off_cb != power_off)
>> +               return -EINVAL;
>> +
>> +       ret = unregister_power_off_handler(&priv->power_off_nb);
>> +       priv->platform_power_off_cb = NULL;
>> +
>> +       return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(unregister_platform_power_off);
>> +
>> +/**
>> + *     do_kernel_power_off - Execute kernel power-off handler call chain
>> + *
>> + *     Calls functions registered with register_power_off_handler.
>> + *
>> + *     Expected to be called as last step of the power-off sequence.
>> + *
>> + *     Powers off the system immediately if a power-off handler function has
>> + *     been registered. Otherwise does nothing.
>> + */
>> +void do_kernel_power_off(void)
>> +{
>> +       /* legacy pm_power_off() is unchained and has highest priority */
>> +       if (pm_power_off && pm_power_off != dummy_pm_power_off)
>> +               return pm_power_off();
>> +
>> +       blocking_notifier_call_chain(&power_off_handler_list, POWEROFF_NORMAL,
>> +                                    NULL);
>> +}
>> +
>> +static void do_kernel_power_off_prepare(void)
>> +{
>> +       /* legacy pm_power_off_prepare() is unchained and has highest priority */
>> +       if (pm_power_off_prepare)
>> +               return pm_power_off_prepare();
>> +
>> +       blocking_notifier_call_chain(&power_off_handler_list, POWEROFF_PREPARE,
>> +                                    NULL);
>> +}
>> +
>>  /**
>>   *     kernel_power_off - power_off the system
>>   *
>> @@ -304,8 +893,7 @@ EXPORT_SYMBOL_GPL(kernel_halt);
>>  void kernel_power_off(void)
>>  {
>>         kernel_shutdown_prepare(SYSTEM_POWER_OFF);
>> -       if (pm_power_off_prepare)
>> -               pm_power_off_prepare();
>> +       do_kernel_power_off_prepare();
>>         migrate_to_reboot_cpu();
>>         syscore_shutdown();
>>         pr_emerg("Power down\n");
>> @@ -314,6 +902,16 @@ void kernel_power_off(void)
>>  }
>>  EXPORT_SYMBOL_GPL(kernel_power_off);
>>
>> +bool kernel_can_power_off(void)
>> +{
>> +       if (!pm_power_off &&
>> +           blocking_notifier_call_chain_is_empty(&power_off_handler_list))
>> +               return false;
>> +
>> +       return true;
> 
> return pm_power_off ||
> blocking_notifier_call_chain_is_empty(&power_off_handler_list);

Thank you for the thorough review!



More information about the linux-riscv mailing list