Re: [RFC PATCH v4 2/2] PM: domains: Skip disabling unused until sync state

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 5 May 2023 at 17:02, Abel Vesa <abel.vesa@xxxxxxxxxx> wrote:
>
> By storing the status of the domain at boot, specified by the provider,
> we can decide to skip powering 'off' the domain on the late initcall,
> strictly based on the status boot being 'on' or 'unknown', and then
> assume the provider will disable it from its sync state callback.
> Also, provide a generic genpd sync state callback for those providers
> that only need that when they state synced.

If I understand correctly, this means that providers that don't use
the sync state callback, will be kept powered-on until the late
initcall, even if those could be turned off at an earlier point. Is
this really what we want?

>
> Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxx>
> ---
>  drivers/base/power/domain.c | 51 +++++++++++++++++++++++++++++++++++--
>  include/linux/pm_domain.h   |  5 ++++
>  2 files changed, 54 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 33a3945c023e..9cc0ce43b47b 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -125,6 +125,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
>  #define genpd_unlock(p)                        p->lock_ops->unlock(p)
>
>  #define genpd_status_on(genpd)         (genpd->status == GENPD_STATE_ON)
> +#define genpd_boot_keep_on(genpd)      (!(genpd->boot_status == GENPD_STATE_OFF))

This seems a bit unnecessarily complicated. Can't we just use bool in
the genpd struct, to track whether we should allow powering off or
not, based upon the boot condition.

>  #define genpd_is_irq_safe(genpd)       (genpd->flags & GENPD_FLAG_IRQ_SAFE)
>  #define genpd_is_always_on(genpd)      (genpd->flags & GENPD_FLAG_ALWAYS_ON)
>  #define genpd_is_active_wakeup(genpd)  (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
> @@ -654,6 +655,29 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
>         queue_work(pm_wq, &genpd->power_off_work);
>  }
>
> +/**
> + * pm_genpd_power_off_unused_sync_state - Power off all domains for provider.
> + * @dev: Provider's device.
> + *
> + * Request power off for all unused domains of the provider.
> + * This should be used exclusively as sync state callback for genpd providers.
> + */
> +void pm_genpd_power_off_unused_sync_state(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       mutex_lock(&gpd_list_lock);
> +
> +       list_for_each_entry(genpd, &gpd_list, gpd_list_node)

It looks like we need the of_genpd_mutex here as well, as it's really
the list of providers that we want to protect too.

> +               if (genpd->provider->dev == dev && genpd_boot_keep_on(genpd)) {
> +                       genpd->boot_status = GENPD_STATE_OFF;

This needs to be done while holding the genpd's lock.

> +                       genpd_queue_power_off_work(genpd);
> +               }
> +
> +       mutex_unlock(&gpd_list_lock);
> +}
> +EXPORT_SYMBOL_GPL(pm_genpd_power_off_unused_sync_state);
> +
>  /**
>   * genpd_power_off - Remove power from a given PM domain.
>   * @genpd: PM domain to power down.
> @@ -674,6 +698,12 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
>         unsigned int not_suspended = 0;
>         int ret;
>
> +       /*
> +        * If the domain was left enabled at boot stage,
> +        * abort power off until sync state is reached.
> +        */
> +       if (genpd_boot_keep_on(genpd))
> +               return -EBUSY;
>         /*
>          * Do not try to power off the domain in the following situations:
>          * (1) The domain is already in the "power off" state.
> @@ -763,6 +793,12 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
>         struct gpd_link *link;
>         int ret = 0;
>
> +       /*
> +        * Just in case this is the first power on request, mark the boot
> +        * status of it as 'off'.
> +        */
> +       genpd->boot_status = GENPD_STATE_OFF;
> +
>         if (genpd_status_on(genpd))
>                 return 0;
>
> @@ -1095,8 +1131,16 @@ static int __init genpd_power_off_unused(void)
>
>         mutex_lock(&gpd_list_lock);
>
> +       /*
> +        * If the provider has registered a 'sync state' callback,
> +        * assume that callback will power off its registered unused domains,
> +        * otherwise we power them off from here.
> +        */
>         list_for_each_entry(genpd, &gpd_list, gpd_list_node)
> -               genpd_queue_power_off_work(genpd);
> +               if (!dev_has_sync_state(&genpd->dev)) {

The genpd->dev isn't really the one that is used by the genpd provider
driver, hence this doesn't work.

In the code you introduced above, you used the "genpd->provider->dev",
which is probably what we want here too, right?

> +                       genpd->boot_status = GENPD_STATE_OFF;

Updating this needs to be protected by the genpd's lock.

> +                       genpd_queue_power_off_work(genpd);
> +               }

The above said - I am concerned about having to hold each genpd's lock
in this path.

I realize that we need to update the genpd->boot_status at some point,
but let me think a bit if we can do that in a slightly better way,
without holding all the locks.

>
>         mutex_unlock(&gpd_list_lock);
>
> @@ -1124,6 +1168,9 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
>  {
>         struct gpd_link *link;
>
> +       if (genpd_boot_keep_on(genpd))
> +               return;
> +
>         if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
>                 return;
>
> @@ -2064,7 +2111,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
>         genpd->gov = gov;
>         INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
>         atomic_set(&genpd->sd_count, 0);
> -       genpd->status = boot_status;
> +       genpd->status = genpd->boot_status = boot_status;
>         genpd->device_count = 0;
>         genpd->provider = NULL;
>         genpd->has_provider = false;
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index c545e44ee52b..86bb531a319c 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -132,6 +132,7 @@ struct generic_pm_domain {
>         const char *name;
>         atomic_t sd_count;      /* Number of subdomains with power "on" */
>         enum gpd_status status; /* Current state of the domain */
> +       enum gpd_status boot_status;    /* Boot state of the domain */
>         unsigned int device_count;      /* Number of devices */
>         unsigned int suspended_count;   /* System suspend device counter */
>         unsigned int prepared_count;    /* Suspend counter of prepared devices */
> @@ -233,6 +234,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
>                   struct dev_power_governor *gov,
>                   enum gpd_status boot_status);
>  int pm_genpd_remove(struct generic_pm_domain *genpd);
> +void pm_genpd_power_off_unused_sync_state(struct device *dev);
>  int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
>  int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb);
>  int dev_pm_genpd_remove_notifier(struct device *dev);
> @@ -281,6 +283,9 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
>         return -EOPNOTSUPP;
>  }
>
> +static inline void pm_genpd_power_off_unused_sync_state(struct device *dev)
> +{ }
> +
>  static inline int dev_pm_genpd_set_performance_state(struct device *dev,
>                                                      unsigned int state)
>  {

Kind regards
Uffe



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux