ack. will try to fix in v5.guessing opp ptr here.. am I right? if it is about device_opp, it is+/**And this one also has the same problem that find_device_opp() does.
+ * 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();
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.
[...]
guessing opp ptr here.. am I right?+ return opp;As does this one.
+}
+
+/**
+ * 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();
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.
Ack. I missed that :(.. let me relook at the logic yet again. hopefully fix in v5.Does the original point about not needing to free dev_opp resolve this?+static int opp_set_availability(struct opp *opp, bool availability_req)If you decide to rely on reference counts to fix the problem in
+{
+ 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();
find_device_opp(), you will need to check the reference counts here.
Again, please see Documentation/RCU/rcuref.txt.
For the dev_opp case, yes. However, I believe that my point is still
valid for the opp case.
aye..thx. I will make it more verbose. Does the following sound right?+ kfree(opp);By "Locking: RCU", you presumably don't mean that the caller must do
+
+ 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
an rcu_read_lock() -- this would result in a synchronize_rcu() being
invoked in an RCU read-side critical section, which is illegal.
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.
thx.Good catch. Thanks. I would rather change the return to int and pass+void opp_init_cpufreq_table(struct device *dev,How does the caller tell that the allocation failed? Should the caller
+ 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;
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?
proper errors to caller so that they can handle it appropriately.
Works for me!