From: Nishanth Menon <nm@ti.com>
To: "paulmck@linux.vnet.ibm.com" <paulmck@linux.vnet.ibm.com>
Cc: linux-pm <linux-pm@lists.linux-foundation.org>,
lkml <linux-kernel@vger.kernel.org>,
linux-arm <linux-arm-kernel@lists.infradead.org>,
linux-omap <linux-omap@vger.kernel.org>
Subject: Re: [PATCH v4] power: introduce library for device-specific OPPs
Date: Mon, 27 Sep 2010 09:29:05 -0500 [thread overview]
Message-ID: <4CA0AA31.3030801@ti.com> (raw)
In-Reply-To: <20100924214003.GL2375@linux.vnet.ibm.com>
Paul E. McKenney had written, on 09/24/2010 04:40 PM, the following:
[...]
>>>> +/**
>>>> + * opp_find_freq_ceil() - Search for an rounded ceil freq
>>>> + * @dev: device for which we do this operation
>>>> + * @freq: Start frequency
>>>> + *
>>>> + * Search for the matching ceil *available* OPP from a starting freq
>>>> + * for a device.
>>>> + *
>>>> + * Returns matching *opp and refreshes *freq accordingly, else returns
>>>> + * ERR_PTR in case of error and should be handled using IS_ERR.
>>>> + *
>>>> + * Locking: RCU reader.
>>>> + */
>>>> +struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
>>>> +{
>>>> + struct device_opp *dev_opp;
>>>> + struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
>>>> +
>>>> + if (!dev || !freq) {
>>>> + pr_err("%s: invalid param dev=%p freq=%p\n", __func__,
>>>> + dev, freq);
>>>> + return ERR_PTR(-EINVAL);
>>>> + }
>>>> +
>>>> + dev_opp = find_device_opp(dev);
>>>> + if (IS_ERR(dev_opp))
>>>> + return opp;
>>>> +
>>>> + rcu_read_lock();
>>>> + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
>>>> + if (temp_opp->available && temp_opp->rate >= *freq) {
>>>> + opp = temp_opp;
>>>> + *freq = opp->rate;
>>>> + break;
>>>> + }
>>>> + }
>>>> + rcu_read_unlock();
>>> And this one also has the same problem that find_device_opp() does.
>> guessing opp ptr here.. am I right? if it is about device_opp, it is
>> not going to be freed as I mentioned above - at least we dont give
>> an function to update(hence free) it.
>
> It really does look to me that you are passing a pointer to the thing
> being freed out of an RCU read-side critical section. If you are really
> doing this, you do need to do something to fix it. I outlined some of
> the options earlier.
ack. will try to fix in v5.
>
>>>> + return opp;
>>>> +}
>>>> +
>>>> +/**
>>>> + * opp_find_freq_floor() - Search for a rounded floor freq
>>>> + * @dev: device for which we do this operation
>>>> + * @freq: Start frequency
>>>> + *
>>>> + * Search for the matching floor *available* OPP from a starting freq
>>>> + * for a device.
>>>> + *
>>>> + * Returns matching *opp and refreshes *freq accordingly, else returns
>>>> + * ERR_PTR in case of error and should be handled using IS_ERR.
>>>> + *
>>>> + * Locking: RCU reader.
>>>> + */
>>>> +struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
>>>> +{
>>>> + struct device_opp *dev_opp;
>>>> + struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
>>>> +
>>>> + if (!dev || !freq) {
>>>> + pr_err("%s: invalid param dev=%p freq=%p\n", __func__,
>>>> + dev, freq);
>>>> + return ERR_PTR(-EINVAL);
>>>> + }
>>>> +
>>>> + dev_opp = find_device_opp(dev);
>>>> + if (IS_ERR(dev_opp))
>>>> + return opp;
>>>> +
>>>> + rcu_read_lock();
>>>> + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
>>>> + if (temp_opp->available) {
>>>> + /* go to the next node, before choosing prev */
>>>> + if (temp_opp->rate > *freq)
>>>> + break;
>>>> + else
>>>> + opp = temp_opp;
>>>> + }
>>>> + }
>>>> + if (!IS_ERR(opp))
>>>> + *freq = opp->rate;
>>>> + rcu_read_unlock();
>>> As does this one.
>> guessing opp ptr here.. am I right?
>
> Again, here it looks to me like you are passing a pointer out of an RCU
> read-side critical section that could be freed out from under you. If
> so, again, this must be fixed.
>
[...]
>>>> +static int opp_set_availability(struct opp *opp, bool availability_req)
>>>> +{
>>>> + struct opp *new_opp, *tmp_opp;
>>>> + bool is_available;
>>>> +
>>>> + if (unlikely(!opp || IS_ERR(opp))) {
>>>> + pr_err("%s: Invalid parameters being passed\n", __func__);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + new_opp = kmalloc(sizeof(struct opp), GFP_KERNEL);
>>>> + if (!new_opp) {
>>>> + pr_warning("%s: unable to allocate opp\n", __func__);
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + mutex_lock(&opp->dev_opp->lock);
>>>> +
>>>> + rcu_read_lock();
>>>> + tmp_opp = rcu_dereference(opp);
>>>> + is_available = tmp_opp->available;
>>>> + rcu_read_unlock();
>>>> +
>>>> + /* Is update really needed? */
>>>> + if (is_available == availability_req) {
>>>> + mutex_unlock(&opp->dev_opp->lock);
>>>> + kfree(tmp_opp);
>>>> + return 0;
>>>> + }
>>>> +
>>>> + *new_opp = *opp;
>>>> + new_opp->available = availability_req;
>>>> + list_replace_rcu(&opp->node, &new_opp->node);
>>>> +
>>>> + mutex_unlock(&opp->dev_opp->lock);
>>>> + synchronize_rcu();
>>> If you decide to rely on reference counts to fix the problem in
>>> find_device_opp(), you will need to check the reference counts here.
>>> Again, please see Documentation/RCU/rcuref.txt.
>> Does the original point about not needing to free dev_opp resolve this?
>
> For the dev_opp case, yes. However, I believe that my point is still
> valid for the opp case.
Ack. I missed that :(.. let me relook at the logic yet again. hopefully
fix in v5.
>
>>>> + kfree(opp);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * opp_enable() - Enable a specific OPP
>>>> + * @opp: Pointer to opp
>>>> + *
>>>> + * Enables a provided opp. If the operation is valid, this returns 0, else the
>>>> + * corresponding error value. It is meant to be used for users an OPP available
>>>> + * after being temporarily made unavailable with opp_disable.
>>>> + *
>>>> + * Locking: RCU, mutex
>>> By "Locking: RCU", you presumably don't mean that the caller must do
>>> an rcu_read_lock() -- this would result in a synchronize_rcu() being
>>> invoked in an RCU read-side critical section, which is illegal.
>> aye..thx. I will make it more verbose. Does the following sound right?
>>
>> Locking used internally: RCU copy-update and read_lock used, mutex
>>
>> and for the readers:
>>
>> Locking used internally: RCU read_lock used
>>
>> or do we need to go all verbatim about the implementation here?
>>
>> I intended the user to know the context in which they can call it,
>> for example, since mutex is used, dont think of using this in
>> interrupt context. since read_locks are already used, dont need to
>> double lock it.. opp library takes care of it's own exclusivity.
>
> I would stick to the constraints on the caller, and describe the internals
> elsewhere, for example, near the data-structure definitions. But tastes
> do vary on this.
okay. let me see how to clean this up.
[..]
>>>> +void opp_init_cpufreq_table(struct device *dev,
>>>> + struct cpufreq_frequency_table **table)
>>>> +{
>>>> + struct device_opp *dev_opp;
>>>> + struct opp *opp;
>>>> + struct cpufreq_frequency_table *freq_table;
>>>> + int i = 0;
>>>> +
>>>> + dev_opp = find_device_opp(dev);
>>>> + if (IS_ERR(dev_opp)) {
>>>> + pr_warning("%s: unable to find device\n", __func__);
>>>> + return;
>>>> + }
>>>> +
>>>> + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) *
>>>> + (opp_get_opp_count(dev) + 1), GFP_ATOMIC);
>>>> + if (!freq_table) {
>>>> + pr_warning("%s: failed to allocate frequency table\n",
>>>> + __func__);
>>>> + return;
>>> How does the caller tell that the allocation failed? Should the caller
>>> set the pointer passed in through the "table" argument to NULL before
>>> calling this function? Or should this function set *table to NULL
>>> before returning in this case?
>> Good catch. Thanks. I would rather change the return to int and pass
>> proper errors to caller so that they can handle it appropriately.
>
> Works for me!
thx.
--
Regards,
Nishanth Menon
next prev parent reply other threads:[~2010-09-27 14:29 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <[PATCH v3] power: introduce library for device-specific OPPs>
2010-09-24 12:50 ` [PATCH v4] power: introduce library for device-specific OPPs Nishanth Menon
2010-09-24 19:37 ` Paul E. McKenney
2010-09-24 21:26 ` Nishanth Menon
2010-09-24 21:40 ` Paul E. McKenney
2010-09-27 14:29 ` Nishanth Menon [this message]
2010-09-25 20:55 ` Rafael J. Wysocki
2010-09-26 0:56 ` Paul E. McKenney
2010-09-27 14:25 ` Nishanth Menon
2010-09-27 19:53 ` Rafael J. Wysocki
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=4CA0AA31.3030801@ti.com \
--to=nm@ti.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=linux-pm@lists.linux-foundation.org \
--cc=paulmck@linux.vnet.ibm.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox