[PATCH 10/10] PM / Domains: Add support for removing nested PM domains by provider
Jon Hunter
jonathanh at nvidia.com
Fri Sep 9 07:04:15 PDT 2016
On 08/09/16 13:30, Ulf Hansson wrote:
> On 16 August 2016 at 11:49, Jon Hunter <jonathanh at nvidia.com> wrote:
>> If a device supports PM domains that are subdomains of another PM
>> domain, then the PM domains should be removed in reverse order to
>> ensure that the subdomains are removed first. Furthermore, if there is
>> more than one provider, then there needs to be a way to remove the
>> domains in reverse order for a specific provider.
>>
>> Add the function of_genpd_remove_tail() to remove the last PM domain
>> added by a given PM domain provider and return the generic_pm_domain
>> structure for the PM domain that was removed.
>>
>> A PM domain should only be removed once the associated PM domain
>> provider has been removed from the list of providers. Otherwise, it
>> could be possible for a client to be associated with a PM domain that
>> could have been removed. Add a helper function to verify if the PM
>> domain provider is present and only allow a PM domain to be removed if
>> the provider has been removed.
>>
>> The function of_genpd_remove_tail() must hold the gpd_list_lock while
>> finding and removing a PM domain. It is natural for
>> of_genpd_remove_tail() to call pm_genpd_remove() once the appropriate
>> PM domain is found to remove it. However, pm_genpd_remove(), also
>> acquires the gpd_list_lock. Therefore, move the core of the function
>> pm_genpd_remove() to a new function genpd_remove() which does not
>> acquire the gpd_list_lock so this can be used by both pm_genpd_remove()
>> and of_genpd_remove_tail().
>>
>> Signed-off-by: Jon Hunter <jonathanh at nvidia.com>
>> ---
>> drivers/base/power/domain.c | 87 +++++++++++++++++++++++++++++++++++++++++----
>> include/linux/pm_domain.h | 7 ++++
>> 2 files changed, 88 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index 0bc145e8e902..b6d1d0441a2d 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -1357,7 +1357,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
>> EXPORT_SYMBOL_GPL(pm_genpd_init);
>>
>> /**
>> - * pm_genpd_remove - Remove a generic I/O PM domain
>> + * genpd_remove - Remove a generic I/O PM domain
>> * @genpd: Pointer to PM domain that is to be removed.
>> *
>> * To remove the PM domain, this function:
>> @@ -1366,9 +1366,10 @@ EXPORT_SYMBOL_GPL(pm_genpd_init);
>> * - Removes the PM domain from the list of registered PM domains.
>> *
>> * The PM domain will only be removed, if it is not a parent to any
>> - * other PM domain and has no devices associated with it.
>> + * other PM domain and has no devices associated with it. Must be called
>> + * with the gpd_list_lock held.
>> */
>> -int pm_genpd_remove(struct generic_pm_domain *genpd)
>> +static int genpd_remove(struct generic_pm_domain *genpd)
>> {
>> struct gpd_link *l, *link;
>> int ret = 0;
>> @@ -1376,12 +1377,10 @@ int pm_genpd_remove(struct generic_pm_domain *genpd)
>> if (IS_ERR_OR_NULL(genpd))
>> return -EINVAL;
>>
>> - mutex_lock(&gpd_list_lock);
>> mutex_lock(&genpd->lock);
>>
>> if (!list_empty(&genpd->master_links) || genpd->device_count) {
>> mutex_unlock(&genpd->lock);
>> - mutex_unlock(&gpd_list_lock);
>> pr_err("%s: unable to remove %s\n", __func__, genpd->name);
>> return -EBUSY;
>> }
>> @@ -1395,11 +1394,25 @@ int pm_genpd_remove(struct generic_pm_domain *genpd)
>> list_del(&genpd->gpd_list_node);
>> mutex_unlock(&genpd->lock);
>> cancel_work_sync(&genpd->power_off_work);
>> - mutex_unlock(&gpd_list_lock);
>> pr_debug("%s: removed %s\n", __func__, genpd->name);
>>
>> return ret;
>> }
>> +
>> +/**
>> + * pm_genpd_remove - Remove a generic I/O PM domain
>> + * @genpd: Pointer to PM domain that is to be removed.
>> + */
>> +int pm_genpd_remove(struct generic_pm_domain *genpd)
>> +{
>> + int ret;
>> +
>> + mutex_lock(&gpd_list_lock);
>> + ret = genpd_remove(genpd);
>> + mutex_unlock(&gpd_list_lock);
>> +
>> + return ret;
>> +}
>> EXPORT_SYMBOL_GPL(pm_genpd_remove);
>
> All above changes could have been made already in the patch when
> adding the pm_genpd_remove() API. Could you please fold these changes
> into that patch instead?
Ok. I was not sure if it would seem odd to add pm_genpd_remove() and
genpd_remove() in the same patch because pm_genpd_remove() is the only
user of genpd_remove(). However, it would simplify the diff and so I am
fine with that.
>>
>> #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
>> @@ -1610,6 +1623,26 @@ void of_genpd_del_provider(struct device_node *np)
>> EXPORT_SYMBOL_GPL(of_genpd_del_provider);
>>
>> /**
>> + * genpd_provider_present() - Verify if a PM domain provider is present
>> + * @np: Device node pointer associated with the PM domain provider
>> + */
>> +static bool genpd_provider_present(struct device_node *np)
>> +{
>> + struct of_genpd_provider *cp;
>> +
>> + mutex_lock(&of_genpd_mutex);
>> + list_for_each_entry(cp, &of_genpd_providers, link) {
>> + if (cp->node == np) {
>> + mutex_unlock(&of_genpd_mutex);
>> + return true;
>> + }
>> + }
>> + mutex_unlock(&of_genpd_mutex);
>> +
>> + return false;
>> +}
>> +
>> +/**
>> * genpd_get_from_provider() - Look-up PM domain
>> * @genpdspec: OF phandle args to use for look-up
>> *
>> @@ -1713,6 +1746,48 @@ out:
>> EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
>>
>> /**
>> + * of_genpd_remove_tail - Remove the last PM domain registered for a provider
>> + * @provider: Pointer to device structure associated with provider
>
> The naming of this function would be okay, if we only have added
> genpds in the gpd_list by using list_add_tail(), although we don't.
> Instead we use list_add() and put them first in the list.
>
> So, unless we change to use list_add_tail() when adding genpds (I
> assume we can do that!), I would rather change the name of this
> function to of_genpd_remove_first().
>
> What option do you prefer?
I think that I would prefer either of_genpd_remove_last() or
of_genpd_remove_one(). Although _first is accurate from the list
perspective it seems odd from the user perspective. I think that _last
is more meaningful as we are removing the last that was added regardless
or how things appear on the list. Alternatively, _one could be a good
compromise.
>> + *
>> + * Find the last PM domain that was added by a particular provider and
>> + * remove this PM domain from the list of PM domains. The provider is
>> + * identified by the 'provider' device structure that is passed. The PM
>> + * domain will only be removed, if the provider associated with domain
>> + * has been removed.
>> + *
>> + * Returns a valid pointer to struct generic_pm_domain on success or
>> + * ERR_PTR() on failure.
>> + */
>> +struct generic_pm_domain *of_genpd_remove_tail(struct device_node *np)
>> +{
>> + struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT);
>> + int ret;
>> +
>> + if (IS_ERR_OR_NULL(np))
>> + return ERR_PTR(-EINVAL);
>> +
>> + mutex_lock(&gpd_list_lock);
>> + list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
>> + if (gpd->provider == &np->fwnode) {
>> + if (!genpd_provider_present(np)) {
>> + ret = genpd_remove(gpd);
>> + genpd = ret ? ERR_PTR(ret) : gpd;
>> + break;
>> + }
>
> Maybe use an "else" here instead to avoid the double "break;".
Ok.
Cheers
Jon
--
nvpublic
More information about the linux-arm-kernel
mailing list