From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Rafael Wysocki <rjw@rjwysocki.net>,
linaro-kernel@lists.linaro.org, linux-pm@vger.kernel.org,
stefan.wahren@i2se.com, nm@ti.com,
linux-arm-kernel@lists.infradead.org, sudeep.holla@arm.com
Subject: Re: [PATCH 4/8] opp: Introduce APIs to remove OPPs
Date: Tue, 25 Nov 2014 08:24:35 -0800 [thread overview]
Message-ID: <20141125162434.GC5050@linux.vnet.ibm.com> (raw)
In-Reply-To: <f815d0855c1bd28d8389554176acd2620e56b3c8.1416910766.git.viresh.kumar@linaro.org>
On Tue, Nov 25, 2014 at 04:04:19PM +0530, Viresh Kumar wrote:
> OPPs are created statically (from DT) or dynamically. Currently we don't free
> OPPs that are created statically, when the module unloads. And so if the module
> is inserted back again, we get warning for duplicate OPPs as the same were
> already present.
>
> Also, there might be a need to remove dynamic OPPs in future and so API for that
> is also added.
>
> This patch adds helper APIs to remove/free existing static and dynamic OPPs.
>
> Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
>
> ---
> @Paul/Rafael: Do we need to use call_srcu() instead of kfree_rcu() in
> opp_set_availability()? I left it as it looked a bit different.
> srcu_notifier_call_chain() is getting called after deleting the node.
If the data is alway accessed under an SRCU read-side critical section,
then you do need call_srcu() when freeing it -- as you pointed out,
-with- the srcu_struct included. ;-)
If the data is accessed under both SRCU and RCU, then things get a bit
more involved.
> drivers/base/power/opp.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm_opp.h | 12 +++++-
> 2 files changed, 113 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index b249b01..7ae4db5 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -79,6 +79,7 @@ struct dev_pm_opp {
> * however addition is possible and is secured by dev_opp_list_lock
> * @dev: device pointer
> * @srcu_head: notifier head to notify the OPP availability changes.
> + * @rcu_head: RCU callback head used for deferred freeing
> * @opp_list: list of opps
> *
> * This is an internal data structure maintaining the link to opps attached to
> @@ -90,6 +91,7 @@ struct device_opp {
>
> struct device *dev;
> struct srcu_notifier_head srcu_head;
> + struct rcu_head rcu_head;
> struct list_head opp_list;
> };
>
> @@ -498,6 +500,76 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
> }
> EXPORT_SYMBOL_GPL(dev_pm_opp_add);
>
> +static void kfree_opp_rcu(struct rcu_head *head)
> +{
> + struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head);
> +
> + kfree(opp);
> +}
> +
> +static void kfree_device_rcu(struct rcu_head *head)
> +{
> + struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head);
> +
> + kfree(device_opp);
> +}
> +
> +void __dev_pm_opp_remove(struct device_opp *dev_opp, struct dev_pm_opp *opp)
> +{
> + /*
> + * Notify the changes in the availability of the operable
> + * frequency/voltage list.
> + */
> + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
> + list_del_rcu(&opp->node);
> + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu);
I am guessing that opp->node is being removed from the list traversed
by srcu_notifier_call_chain()... (Looks that way below.)
> +
> + if (list_empty(&dev_opp->opp_list)) {
Does the above want to be "if (!list_empty(&dev_opp->opp_list)) {"?
Thanx, Paul
> + list_del_rcu(&dev_opp->node);
> + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head,
> + kfree_device_rcu);
> + }
> +}
> +
> +/**
> + * dev_pm_opp_remove() - Remove an OPP from OPP list
> + * @dev: device for which we do this operation
> + * @freq: OPP to remove with matching 'freq'
> + *
> + * This function removes an opp from the opp list.
> + */
> +void dev_pm_opp_remove(struct device *dev, unsigned long freq)
> +{
> + struct dev_pm_opp *opp;
> + struct device_opp *dev_opp;
> + bool found = false;
> +
> + /* Hold our list modification lock here */
> + mutex_lock(&dev_opp_list_lock);
> +
> + dev_opp = find_device_opp(dev);
> + if (IS_ERR(dev_opp))
> + goto unlock;
> +
> + list_for_each_entry(opp, &dev_opp->opp_list, node) {
> + if (opp->rate == freq) {
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
> + __func__, freq);
> + goto unlock;
> + }
> +
> + __dev_pm_opp_remove(dev_opp, opp);
> +unlock:
> + mutex_unlock(&dev_opp_list_lock);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
> +
> /**
> * opp_set_availability() - helper to set the availability of an opp
> * @dev: device for which we do this operation
> @@ -687,4 +759,34 @@ int of_init_opp_table(struct device *dev)
> return 0;
> }
> EXPORT_SYMBOL_GPL(of_init_opp_table);
> +
> +/**
> + * of_free_opp_table() - Free OPP table entries created from static DT entries
> + * @dev: device pointer used to lookup device OPPs.
> + *
> + * Free OPPs created using static entries present in DT.
> + */
> +void of_free_opp_table(struct device *dev)
> +{
> + struct device_opp *dev_opp = find_device_opp(dev);
> + struct dev_pm_opp *opp, *tmp;
> +
> + /* Check for existing list for 'dev' */
> + dev_opp = find_device_opp(dev);
> + if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev),
> + PTR_ERR(dev_opp)))
> + return;
> +
> + /* Hold our list modification lock here */
> + mutex_lock(&dev_opp_list_lock);
> +
> + /* Free static OPPs */
> + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) {
> + if (!opp->dynamic)
> + __dev_pm_opp_remove(dev_opp, opp);
> + }
> +
> + mutex_unlock(&dev_opp_list_lock);
> +}
> +EXPORT_SYMBOL_GPL(of_free_opp_table);
> #endif
> diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
> index 0330217..cec2d45 100644
> --- a/include/linux/pm_opp.h
> +++ b/include/linux/pm_opp.h
> @@ -21,7 +21,7 @@ struct dev_pm_opp;
> struct device;
>
> enum dev_pm_opp_event {
> - OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
> + OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
> };
>
> #if defined(CONFIG_PM_OPP)
> @@ -44,6 +44,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
>
> int dev_pm_opp_add(struct device *dev, unsigned long freq,
> unsigned long u_volt);
> +void dev_pm_opp_remove(struct device *dev, unsigned long freq);
>
> int dev_pm_opp_enable(struct device *dev, unsigned long freq);
>
> @@ -90,6 +91,10 @@ static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
> return -EINVAL;
> }
>
> +static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq)
> +{
> +}
> +
> static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
> {
> return 0;
> @@ -109,11 +114,16 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
>
> #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
> int of_init_opp_table(struct device *dev);
> +void of_free_opp_table(struct device *dev);
> #else
> static inline int of_init_opp_table(struct device *dev)
> {
> return -EINVAL;
> }
> +
> +static inline void of_free_opp_table(struct device *dev)
> +{
> +}
> #endif
>
> #endif /* __LINUX_OPP_H__ */
> --
> 2.0.3.693.g996b0fd
>
WARNING: multiple messages have this Message-ID (diff)
From: paulmck@linux.vnet.ibm.com (Paul E. McKenney)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 4/8] opp: Introduce APIs to remove OPPs
Date: Tue, 25 Nov 2014 08:24:35 -0800 [thread overview]
Message-ID: <20141125162434.GC5050@linux.vnet.ibm.com> (raw)
In-Reply-To: <f815d0855c1bd28d8389554176acd2620e56b3c8.1416910766.git.viresh.kumar@linaro.org>
On Tue, Nov 25, 2014 at 04:04:19PM +0530, Viresh Kumar wrote:
> OPPs are created statically (from DT) or dynamically. Currently we don't free
> OPPs that are created statically, when the module unloads. And so if the module
> is inserted back again, we get warning for duplicate OPPs as the same were
> already present.
>
> Also, there might be a need to remove dynamic OPPs in future and so API for that
> is also added.
>
> This patch adds helper APIs to remove/free existing static and dynamic OPPs.
>
> Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
>
> ---
> @Paul/Rafael: Do we need to use call_srcu() instead of kfree_rcu() in
> opp_set_availability()? I left it as it looked a bit different.
> srcu_notifier_call_chain() is getting called after deleting the node.
If the data is alway accessed under an SRCU read-side critical section,
then you do need call_srcu() when freeing it -- as you pointed out,
-with- the srcu_struct included. ;-)
If the data is accessed under both SRCU and RCU, then things get a bit
more involved.
> drivers/base/power/opp.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm_opp.h | 12 +++++-
> 2 files changed, 113 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index b249b01..7ae4db5 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -79,6 +79,7 @@ struct dev_pm_opp {
> * however addition is possible and is secured by dev_opp_list_lock
> * @dev: device pointer
> * @srcu_head: notifier head to notify the OPP availability changes.
> + * @rcu_head: RCU callback head used for deferred freeing
> * @opp_list: list of opps
> *
> * This is an internal data structure maintaining the link to opps attached to
> @@ -90,6 +91,7 @@ struct device_opp {
>
> struct device *dev;
> struct srcu_notifier_head srcu_head;
> + struct rcu_head rcu_head;
> struct list_head opp_list;
> };
>
> @@ -498,6 +500,76 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
> }
> EXPORT_SYMBOL_GPL(dev_pm_opp_add);
>
> +static void kfree_opp_rcu(struct rcu_head *head)
> +{
> + struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head);
> +
> + kfree(opp);
> +}
> +
> +static void kfree_device_rcu(struct rcu_head *head)
> +{
> + struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head);
> +
> + kfree(device_opp);
> +}
> +
> +void __dev_pm_opp_remove(struct device_opp *dev_opp, struct dev_pm_opp *opp)
> +{
> + /*
> + * Notify the changes in the availability of the operable
> + * frequency/voltage list.
> + */
> + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
> + list_del_rcu(&opp->node);
> + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu);
I am guessing that opp->node is being removed from the list traversed
by srcu_notifier_call_chain()... (Looks that way below.)
> +
> + if (list_empty(&dev_opp->opp_list)) {
Does the above want to be "if (!list_empty(&dev_opp->opp_list)) {"?
Thanx, Paul
> + list_del_rcu(&dev_opp->node);
> + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head,
> + kfree_device_rcu);
> + }
> +}
> +
> +/**
> + * dev_pm_opp_remove() - Remove an OPP from OPP list
> + * @dev: device for which we do this operation
> + * @freq: OPP to remove with matching 'freq'
> + *
> + * This function removes an opp from the opp list.
> + */
> +void dev_pm_opp_remove(struct device *dev, unsigned long freq)
> +{
> + struct dev_pm_opp *opp;
> + struct device_opp *dev_opp;
> + bool found = false;
> +
> + /* Hold our list modification lock here */
> + mutex_lock(&dev_opp_list_lock);
> +
> + dev_opp = find_device_opp(dev);
> + if (IS_ERR(dev_opp))
> + goto unlock;
> +
> + list_for_each_entry(opp, &dev_opp->opp_list, node) {
> + if (opp->rate == freq) {
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
> + __func__, freq);
> + goto unlock;
> + }
> +
> + __dev_pm_opp_remove(dev_opp, opp);
> +unlock:
> + mutex_unlock(&dev_opp_list_lock);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
> +
> /**
> * opp_set_availability() - helper to set the availability of an opp
> * @dev: device for which we do this operation
> @@ -687,4 +759,34 @@ int of_init_opp_table(struct device *dev)
> return 0;
> }
> EXPORT_SYMBOL_GPL(of_init_opp_table);
> +
> +/**
> + * of_free_opp_table() - Free OPP table entries created from static DT entries
> + * @dev: device pointer used to lookup device OPPs.
> + *
> + * Free OPPs created using static entries present in DT.
> + */
> +void of_free_opp_table(struct device *dev)
> +{
> + struct device_opp *dev_opp = find_device_opp(dev);
> + struct dev_pm_opp *opp, *tmp;
> +
> + /* Check for existing list for 'dev' */
> + dev_opp = find_device_opp(dev);
> + if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev),
> + PTR_ERR(dev_opp)))
> + return;
> +
> + /* Hold our list modification lock here */
> + mutex_lock(&dev_opp_list_lock);
> +
> + /* Free static OPPs */
> + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) {
> + if (!opp->dynamic)
> + __dev_pm_opp_remove(dev_opp, opp);
> + }
> +
> + mutex_unlock(&dev_opp_list_lock);
> +}
> +EXPORT_SYMBOL_GPL(of_free_opp_table);
> #endif
> diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
> index 0330217..cec2d45 100644
> --- a/include/linux/pm_opp.h
> +++ b/include/linux/pm_opp.h
> @@ -21,7 +21,7 @@ struct dev_pm_opp;
> struct device;
>
> enum dev_pm_opp_event {
> - OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
> + OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
> };
>
> #if defined(CONFIG_PM_OPP)
> @@ -44,6 +44,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
>
> int dev_pm_opp_add(struct device *dev, unsigned long freq,
> unsigned long u_volt);
> +void dev_pm_opp_remove(struct device *dev, unsigned long freq);
>
> int dev_pm_opp_enable(struct device *dev, unsigned long freq);
>
> @@ -90,6 +91,10 @@ static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
> return -EINVAL;
> }
>
> +static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq)
> +{
> +}
> +
> static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
> {
> return 0;
> @@ -109,11 +114,16 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
>
> #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
> int of_init_opp_table(struct device *dev);
> +void of_free_opp_table(struct device *dev);
> #else
> static inline int of_init_opp_table(struct device *dev)
> {
> return -EINVAL;
> }
> +
> +static inline void of_free_opp_table(struct device *dev)
> +{
> +}
> #endif
>
> #endif /* __LINUX_OPP_H__ */
> --
> 2.0.3.693.g996b0fd
>
next prev parent reply other threads:[~2014-11-25 16:24 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-25 10:34 [PATCH 0/8] OPP: Remove OPPs when not in use Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 1/8] opp: rename 'head' as 'rcu_head' or 'srcu_head' based on its type Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 2/8] opp: don't match for existing OPPs when list is empty Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 3/8] opp: mark OPPs as 'static' or 'dynamic' Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 4/8] opp: Introduce APIs to remove OPPs Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 16:24 ` Paul E. McKenney [this message]
2014-11-25 16:24 ` Paul E. McKenney
2014-11-25 17:16 ` Viresh Kumar
2014-11-25 17:16 ` Viresh Kumar
2014-11-25 17:39 ` Paul E. McKenney
2014-11-25 17:39 ` Paul E. McKenney
2014-11-26 6:29 ` Viresh Kumar
2014-11-26 6:29 ` Viresh Kumar
2014-11-26 9:17 ` Viresh Kumar
2014-11-26 9:17 ` Viresh Kumar
2014-11-26 22:32 ` Rafael J. Wysocki
2014-11-26 22:32 ` Rafael J. Wysocki
2014-11-27 0:26 ` Viresh Kumar
2014-11-27 0:26 ` Viresh Kumar
2014-11-27 0:48 ` Rafael J. Wysocki
2014-11-27 0:48 ` Rafael J. Wysocki
[not found] ` <1407c1d67e546a09f5dd122de943de45bcd3c846.1417058376.git.viresh.kumar@linaro.org>
2014-11-27 3:24 ` [PATCH V2 " Viresh Kumar
2014-11-27 3:24 ` Viresh Kumar
2014-12-01 23:25 ` Paul E. McKenney
2014-12-01 23:25 ` Paul E. McKenney
2014-11-27 3:24 ` [PATCH V1 5/8] opp: replace kfree_rcu() with call_srcu() in opp_set_availability() Viresh Kumar
2014-11-27 3:24 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 5/8] arm_big_little: free OPP table created during ->init() Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-12-01 7:11 ` Viresh Kumar
2014-12-01 7:11 ` Viresh Kumar
2014-12-01 22:37 ` Rafael J. Wysocki
2014-12-01 22:37 ` Rafael J. Wysocki
2014-12-02 3:46 ` Viresh Kumar
2014-12-02 3:46 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 6/8] cpufreq-dt: " Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 20:15 ` Stefan Wahren
2014-11-25 20:15 ` Stefan Wahren
2014-11-25 10:34 ` [PATCH 7/8] exynos5440: " Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
2014-11-25 10:34 ` [PATCH 8/8] imx6q: " Viresh Kumar
2014-11-25 10:34 ` Viresh Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20141125162434.GC5050@linux.vnet.ibm.com \
--to=paulmck@linux.vnet.ibm.com \
--cc=linaro-kernel@lists.linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-pm@vger.kernel.org \
--cc=nm@ti.com \
--cc=rjw@rjwysocki.net \
--cc=stefan.wahren@i2se.com \
--cc=sudeep.holla@arm.com \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.