Skip to content

Commit bb81451

Browse files
committed
Merge tag 'opp-updates-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull OPP (Operating Performance Points) updates for 6.5 from Viresh Kumar: "- Simplify performance state related logic in the OPP core (Viresh Kumar). - Fix use-after-free and improve locking around lazy_opp_tables (Viresh Kumar and Stephan Gerhold). - Minor cleanups - using dev_err_probe() and rate-limiting debug messages (Andrew Halaney and Adrián Larumbe)." * tag 'opp-updates-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: OPP: Properly propagate error along when failing to get icc_path OPP: Use dev_err_probe() when failing to get icc_path OPP: Simplify the over-designed pstate <-> level dance OPP: pstate is only valid for genpd OPP tables OPP: don't drop performance constraint on OPP table removal OPP: Protect `lazy_opp_tables` list with `opp_table_lock` OPP: Staticize `lazy_opp_tables` in of.c opp: Fix use-after-free in lazy_opp_tables after probe deferral OPP: rate-limit debug messages when no change in OPP is required
2 parents 40e8e98 + 5fb2864 commit bb81451

4 files changed

Lines changed: 48 additions & 43 deletions

File tree

drivers/opp/core.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@
2929
*/
3030
LIST_HEAD(opp_tables);
3131

32-
/* OPP tables with uninitialized required OPPs */
33-
LIST_HEAD(lazy_opp_tables);
34-
3532
/* Lock to allow exclusive modification to the device and opp lists */
3633
DEFINE_MUTEX(opp_table_lock);
3734
/* Flag indicating that opp_tables list is being updated at the moment */
@@ -230,17 +227,25 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level);
230227
unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp,
231228
unsigned int index)
232229
{
230+
struct opp_table *opp_table = opp->opp_table;
231+
233232
if (IS_ERR_OR_NULL(opp) || !opp->available ||
234-
index >= opp->opp_table->required_opp_count) {
233+
index >= opp_table->required_opp_count) {
235234
pr_err("%s: Invalid parameters\n", __func__);
236235
return 0;
237236
}
238237

239238
/* required-opps not fully initialized yet */
240-
if (lazy_linking_pending(opp->opp_table))
239+
if (lazy_linking_pending(opp_table))
241240
return 0;
242241

243-
return opp->required_opps[index]->pstate;
242+
/* The required OPP table must belong to a genpd */
243+
if (unlikely(!opp_table->required_opp_tables[index]->is_genpd)) {
244+
pr_err("%s: Performance state is only valid for genpds.\n", __func__);
245+
return 0;
246+
}
247+
248+
return opp->required_opps[index]->level;
244249
}
245250
EXPORT_SYMBOL_GPL(dev_pm_opp_get_required_pstate);
246251

@@ -938,7 +943,7 @@ static int _set_opp_bw(const struct opp_table *opp_table,
938943
static int _set_performance_state(struct device *dev, struct device *pd_dev,
939944
struct dev_pm_opp *opp, int i)
940945
{
941-
unsigned int pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
946+
unsigned int pstate = likely(opp) ? opp->required_opps[i]->level: 0;
942947
int ret;
943948

944949
if (!pd_dev)
@@ -1091,7 +1096,7 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
10911096

10921097
/* Return early if nothing to do */
10931098
if (!forced && old_opp == opp && opp_table->enabled) {
1094-
dev_dbg(dev, "%s: OPPs are same, nothing to do\n", __func__);
1099+
dev_dbg_ratelimited(dev, "%s: OPPs are same, nothing to do\n", __func__);
10951100
return 0;
10961101
}
10971102

@@ -1358,7 +1363,10 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
13581363
return opp_table;
13591364

13601365
remove_opp_dev:
1366+
_of_clear_opp_table(opp_table);
13611367
_remove_opp_dev(opp_dev, opp_table);
1368+
mutex_destroy(&opp_table->genpd_virt_dev_lock);
1369+
mutex_destroy(&opp_table->lock);
13621370
err:
13631371
kfree(opp_table);
13641372
return ERR_PTR(ret);
@@ -1522,16 +1530,8 @@ static void _opp_table_kref_release(struct kref *kref)
15221530

15231531
WARN_ON(!list_empty(&opp_table->opp_list));
15241532

1525-
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
1526-
/*
1527-
* The OPP table is getting removed, drop the performance state
1528-
* constraints.
1529-
*/
1530-
if (opp_table->genpd_performance_state)
1531-
dev_pm_genpd_set_performance_state((struct device *)(opp_dev->dev), 0);
1532-
1533+
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node)
15331534
_remove_opp_dev(opp_dev, opp_table);
1534-
}
15351535

15361536
mutex_destroy(&opp_table->genpd_virt_dev_lock);
15371537
mutex_destroy(&opp_table->lock);
@@ -2704,6 +2704,12 @@ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table,
27042704
if (!src_table || !src_table->required_opp_count)
27052705
return pstate;
27062706

2707+
/* Both OPP tables must belong to genpds */
2708+
if (unlikely(!src_table->is_genpd || !dst_table->is_genpd)) {
2709+
pr_err("%s: Performance state is only valid for genpds.\n", __func__);
2710+
return -EINVAL;
2711+
}
2712+
27072713
/* required-opps not fully initialized yet */
27082714
if (lazy_linking_pending(src_table))
27092715
return -EBUSY;
@@ -2722,8 +2728,8 @@ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table,
27222728
mutex_lock(&src_table->lock);
27232729

27242730
list_for_each_entry(opp, &src_table->opp_list, node) {
2725-
if (opp->pstate == pstate) {
2726-
dest_pstate = opp->required_opps[i]->pstate;
2731+
if (opp->level == pstate) {
2732+
dest_pstate = opp->required_opps[i]->level;
27272733
goto unlock;
27282734
}
27292735
}

drivers/opp/debugfs.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
152152
debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic);
153153
debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo);
154154
debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
155-
debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
156155
debugfs_create_u32("level", S_IRUGO, d, &opp->level);
157156
debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
158157
&opp->clock_latency_ns);

drivers/opp/of.c

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
#include "opp.h"
2323

24+
/* OPP tables with uninitialized required OPPs, protected by opp_table_lock */
25+
static LIST_HEAD(lazy_opp_tables);
26+
2427
/*
2528
* Returns opp descriptor node for a device node, caller must
2629
* do of_node_put().
@@ -145,7 +148,10 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
145148

146149
opp_table->required_opp_count = 0;
147150
opp_table->required_opp_tables = NULL;
151+
152+
mutex_lock(&opp_table_lock);
148153
list_del(&opp_table->lazy);
154+
mutex_unlock(&opp_table_lock);
149155
}
150156

151157
/*
@@ -194,8 +200,15 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
194200
}
195201

196202
/* Let's do the linking later on */
197-
if (lazy)
203+
if (lazy) {
204+
/*
205+
* The OPP table is not held while allocating the table, take it
206+
* now to avoid corruption to the lazy_opp_tables list.
207+
*/
208+
mutex_lock(&opp_table_lock);
198209
list_add(&opp_table->lazy, &lazy_opp_tables);
210+
mutex_unlock(&opp_table_lock);
211+
}
199212
else
200213
_update_set_required_opps(opp_table);
201214

@@ -500,11 +513,7 @@ int dev_pm_opp_of_find_icc_paths(struct device *dev,
500513
for (i = 0; i < num_paths; i++) {
501514
paths[i] = of_icc_get_by_index(dev, i);
502515
if (IS_ERR(paths[i])) {
503-
ret = PTR_ERR(paths[i]);
504-
if (ret != -EPROBE_DEFER) {
505-
dev_err(dev, "%s: Unable to get path%d: %d\n",
506-
__func__, i, ret);
507-
}
516+
ret = dev_err_probe(dev, PTR_ERR(paths[i]), "%s: Unable to get path%d\n", __func__, i);
508517
goto err;
509518
}
510519
}
@@ -932,9 +941,6 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
932941
if (ret)
933942
goto free_required_opps;
934943

935-
if (opp_table->is_genpd)
936-
new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp);
937-
938944
ret = _opp_add(dev, new_opp, opp_table);
939945
if (ret) {
940946
/* Don't return error for duplicate OPPs */
@@ -1021,14 +1027,6 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
10211027
goto remove_static_opp;
10221028
}
10231029

1024-
list_for_each_entry(opp, &opp_table->opp_list, node) {
1025-
/* Any non-zero performance state would enable the feature */
1026-
if (opp->pstate) {
1027-
opp_table->genpd_performance_state = true;
1028-
break;
1029-
}
1030-
}
1031-
10321030
lazy_link_required_opp_table(opp_table);
10331031

10341032
return 0;
@@ -1387,9 +1385,15 @@ int of_get_required_opp_performance_state(struct device_node *np, int index)
13871385
goto put_required_np;
13881386
}
13891387

1388+
/* The OPP tables must belong to a genpd */
1389+
if (unlikely(!opp_table->is_genpd)) {
1390+
pr_err("%s: Performance state is only valid for genpds.\n", __func__);
1391+
goto put_required_np;
1392+
}
1393+
13901394
opp = _find_opp_of_np(opp_table, required_np);
13911395
if (opp) {
1392-
pstate = opp->pstate;
1396+
pstate = opp->level;
13931397
dev_pm_opp_put(opp);
13941398
}
13951399

drivers/opp/opp.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct regulator;
2626
/* Lock to allow exclusive modification to the device and opp lists */
2727
extern struct mutex opp_table_lock;
2828

29-
extern struct list_head opp_tables, lazy_opp_tables;
29+
extern struct list_head opp_tables;
3030

3131
/* OPP Config flags */
3232
#define OPP_CONFIG_CLK BIT(0)
@@ -78,7 +78,6 @@ struct opp_config_data {
7878
* @turbo: true if turbo (boost) OPP
7979
* @suspend: true if suspend OPP
8080
* @removed: flag indicating that OPP's reference is dropped by OPP core.
81-
* @pstate: Device's power domain's performance state.
8281
* @rates: Frequencies in hertz
8382
* @level: Performance level
8483
* @supplies: Power supplies voltage/current values
@@ -101,7 +100,6 @@ struct dev_pm_opp {
101100
bool turbo;
102101
bool suspend;
103102
bool removed;
104-
unsigned int pstate;
105103
unsigned long *rates;
106104
unsigned int level;
107105

@@ -182,7 +180,6 @@ enum opp_table_access {
182180
* @paths: Interconnect path handles
183181
* @path_count: Number of interconnect paths
184182
* @enabled: Set to true if the device's resources are enabled/configured.
185-
* @genpd_performance_state: Device's power domain support performance state.
186183
* @is_genpd: Marks if the OPP table belongs to a genpd.
187184
* @set_required_opps: Helper responsible to set required OPPs.
188185
* @dentry: debugfs dentry pointer of the real device directory (not links).
@@ -233,7 +230,6 @@ struct opp_table {
233230
struct icc_path **paths;
234231
unsigned int path_count;
235232
bool enabled;
236-
bool genpd_performance_state;
237233
bool is_genpd;
238234
int (*set_required_opps)(struct device *dev,
239235
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down);

0 commit comments

Comments
 (0)