Skip to content

Commit b5d281f

Browse files
Ansuelchanwoochoi
authored andcommitted
PM / devfreq: Rework freq_table to be local to devfreq struct
On a devfreq PROBE_DEFER, the freq_table in the driver profile struct, is never reset and may be leaved in an undefined state. This comes from the fact that we store the freq_table in the driver profile struct that is commonly defined as static and not reset on PROBE_DEFER. We currently skip the reinit of the freq_table if we found it's already defined since a driver may declare his own freq_table. This logic is flawed in the case devfreq core generate a freq_table, set it in the profile struct and then PROBE_DEFER, freeing the freq_table. In this case devfreq will found a NOT NULL freq_table that has been freed, skip the freq_table generation and probe the driver based on the wrong table. To fix this and correctly handle PROBE_DEFER, use a local freq_table and max_state in the devfreq struct and never modify the freq_table present in the profile struct if it does provide it. Fixes: 0ec09ac ("PM / devfreq: Set the freq_table of devfreq device") Cc: [email protected] Signed-off-by: Christian Marangi <[email protected]> Signed-off-by: Chanwoo Choi <[email protected]>
1 parent f44b799 commit b5d281f

File tree

3 files changed

+46
-44
lines changed

3 files changed

+46
-44
lines changed

drivers/devfreq/devfreq.c

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfreq *devfreq,
123123
unsigned long *min_freq,
124124
unsigned long *max_freq)
125125
{
126-
unsigned long *freq_table = devfreq->profile->freq_table;
126+
unsigned long *freq_table = devfreq->freq_table;
127127
s32 qos_min_freq, qos_max_freq;
128128

129129
lockdep_assert_held(&devfreq->lock);
@@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfreq *devfreq,
133133
* The devfreq drivers can initialize this in either ascending or
134134
* descending order and devfreq core supports both.
135135
*/
136-
if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) {
136+
if (freq_table[0] < freq_table[devfreq->max_state - 1]) {
137137
*min_freq = freq_table[0];
138-
*max_freq = freq_table[devfreq->profile->max_state - 1];
138+
*max_freq = freq_table[devfreq->max_state - 1];
139139
} else {
140-
*min_freq = freq_table[devfreq->profile->max_state - 1];
140+
*min_freq = freq_table[devfreq->max_state - 1];
141141
*max_freq = freq_table[0];
142142
}
143143

@@ -169,16 +169,15 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
169169
{
170170
int lev;
171171

172-
for (lev = 0; lev < devfreq->profile->max_state; lev++)
173-
if (freq == devfreq->profile->freq_table[lev])
172+
for (lev = 0; lev < devfreq->max_state; lev++)
173+
if (freq == devfreq->freq_table[lev])
174174
return lev;
175175

176176
return -EINVAL;
177177
}
178178

179179
static int set_freq_table(struct devfreq *devfreq)
180180
{
181-
struct devfreq_dev_profile *profile = devfreq->profile;
182181
struct dev_pm_opp *opp;
183182
unsigned long freq;
184183
int i, count;
@@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq *devfreq)
188187
if (count <= 0)
189188
return -EINVAL;
190189

191-
profile->max_state = count;
192-
profile->freq_table = devm_kcalloc(devfreq->dev.parent,
193-
profile->max_state,
194-
sizeof(*profile->freq_table),
195-
GFP_KERNEL);
196-
if (!profile->freq_table) {
197-
profile->max_state = 0;
190+
devfreq->max_state = count;
191+
devfreq->freq_table = devm_kcalloc(devfreq->dev.parent,
192+
devfreq->max_state,
193+
sizeof(*devfreq->freq_table),
194+
GFP_KERNEL);
195+
if (!devfreq->freq_table)
198196
return -ENOMEM;
199-
}
200197

201-
for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
198+
for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) {
202199
opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);
203200
if (IS_ERR(opp)) {
204-
devm_kfree(devfreq->dev.parent, profile->freq_table);
205-
profile->max_state = 0;
201+
devm_kfree(devfreq->dev.parent, devfreq->freq_table);
206202
return PTR_ERR(opp);
207203
}
208204
dev_pm_opp_put(opp);
209-
profile->freq_table[i] = freq;
205+
devfreq->freq_table[i] = freq;
210206
}
211207

212208
return 0;
@@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
246242

247243
if (lev != prev_lev) {
248244
devfreq->stats.trans_table[
249-
(prev_lev * devfreq->profile->max_state) + lev]++;
245+
(prev_lev * devfreq->max_state) + lev]++;
250246
devfreq->stats.total_trans++;
251247
}
252248

@@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struct device *dev,
835831
if (err < 0)
836832
goto err_dev;
837833
mutex_lock(&devfreq->lock);
834+
} else {
835+
devfreq->freq_table = devfreq->profile->freq_table;
836+
devfreq->max_state = devfreq->profile->max_state;
838837
}
839838

840839
devfreq->scaling_min_freq = find_available_min_freq(devfreq);
@@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struct device *dev,
870869

871870
devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev,
872871
array3_size(sizeof(unsigned int),
873-
devfreq->profile->max_state,
874-
devfreq->profile->max_state),
872+
devfreq->max_state,
873+
devfreq->max_state),
875874
GFP_KERNEL);
876875
if (!devfreq->stats.trans_table) {
877876
mutex_unlock(&devfreq->lock);
@@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
880879
}
881880

882881
devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev,
883-
devfreq->profile->max_state,
882+
devfreq->max_state,
884883
sizeof(*devfreq->stats.time_in_state),
885884
GFP_KERNEL);
886885
if (!devfreq->stats.time_in_state) {
@@ -1666,9 +1665,9 @@ static ssize_t available_frequencies_show(struct device *d,
16661665

16671666
mutex_lock(&df->lock);
16681667

1669-
for (i = 0; i < df->profile->max_state; i++)
1668+
for (i = 0; i < df->max_state; i++)
16701669
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
1671-
"%lu ", df->profile->freq_table[i]);
1670+
"%lu ", df->freq_table[i]);
16721671

16731672
mutex_unlock(&df->lock);
16741673
/* Truncate the trailing space */
@@ -1691,7 +1690,7 @@ static ssize_t trans_stat_show(struct device *dev,
16911690

16921691
if (!df->profile)
16931692
return -EINVAL;
1694-
max_state = df->profile->max_state;
1693+
max_state = df->max_state;
16951694

16961695
if (max_state == 0)
16971696
return sprintf(buf, "Not Supported.\n");
@@ -1708,19 +1707,17 @@ static ssize_t trans_stat_show(struct device *dev,
17081707
len += sprintf(buf + len, " :");
17091708
for (i = 0; i < max_state; i++)
17101709
len += sprintf(buf + len, "%10lu",
1711-
df->profile->freq_table[i]);
1710+
df->freq_table[i]);
17121711

17131712
len += sprintf(buf + len, " time(ms)\n");
17141713

17151714
for (i = 0; i < max_state; i++) {
1716-
if (df->profile->freq_table[i]
1717-
== df->previous_freq) {
1715+
if (df->freq_table[i] == df->previous_freq)
17181716
len += sprintf(buf + len, "*");
1719-
} else {
1717+
else
17201718
len += sprintf(buf + len, " ");
1721-
}
1722-
len += sprintf(buf + len, "%10lu:",
1723-
df->profile->freq_table[i]);
1719+
1720+
len += sprintf(buf + len, "%10lu:", df->freq_table[i]);
17241721
for (j = 0; j < max_state; j++)
17251722
len += sprintf(buf + len, "%10u",
17261723
df->stats.trans_table[(i * max_state) + j]);
@@ -1744,19 +1741,19 @@ static ssize_t trans_stat_store(struct device *dev,
17441741
if (!df->profile)
17451742
return -EINVAL;
17461743

1747-
if (df->profile->max_state == 0)
1744+
if (df->max_state == 0)
17481745
return count;
17491746

17501747
err = kstrtoint(buf, 10, &value);
17511748
if (err || value != 0)
17521749
return -EINVAL;
17531750

17541751
mutex_lock(&df->lock);
1755-
memset(df->stats.time_in_state, 0, (df->profile->max_state *
1752+
memset(df->stats.time_in_state, 0, (df->max_state *
17561753
sizeof(*df->stats.time_in_state)));
17571754
memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int),
1758-
df->profile->max_state,
1759-
df->profile->max_state));
1755+
df->max_state,
1756+
df->max_state));
17601757
df->stats.total_trans = 0;
17611758
df->stats.last_update = get_jiffies_64();
17621759
mutex_unlock(&df->lock);

drivers/devfreq/governor_passive.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,18 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq,
144144
goto out;
145145

146146
/* Use interpolation if required opps is not available */
147-
for (i = 0; i < parent_devfreq->profile->max_state; i++)
148-
if (parent_devfreq->profile->freq_table[i] == *freq)
147+
for (i = 0; i < parent_devfreq->max_state; i++)
148+
if (parent_devfreq->freq_table[i] == *freq)
149149
break;
150150

151-
if (i == parent_devfreq->profile->max_state)
151+
if (i == parent_devfreq->max_state)
152152
return -EINVAL;
153153

154-
if (i < devfreq->profile->max_state) {
155-
child_freq = devfreq->profile->freq_table[i];
154+
if (i < devfreq->max_state) {
155+
child_freq = devfreq->freq_table[i];
156156
} else {
157-
count = devfreq->profile->max_state;
158-
child_freq = devfreq->profile->freq_table[count - 1];
157+
count = devfreq->max_state;
158+
child_freq = devfreq->freq_table[count - 1];
159159
}
160160

161161
out:

include/linux/devfreq.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ struct devfreq_stats {
148148
* reevaluate operable frequencies. Devfreq users may use
149149
* devfreq.nb to the corresponding register notifier call chain.
150150
* @work: delayed work for load monitoring.
151+
* @freq_table: current frequency table used by the devfreq driver.
152+
* @max_state: count of entry present in the frequency table.
151153
* @previous_freq: previously configured frequency value.
152154
* @last_status: devfreq user device info, performance statistics
153155
* @data: Private data of the governor. The devfreq framework does not
@@ -185,6 +187,9 @@ struct devfreq {
185187
struct notifier_block nb;
186188
struct delayed_work work;
187189

190+
unsigned long *freq_table;
191+
unsigned int max_state;
192+
188193
unsigned long previous_freq;
189194
struct devfreq_dev_status last_status;
190195

0 commit comments

Comments
 (0)